refactor(frontend): replace legacy document typing for billing and categories
This commit is contained in:
parent
6cde64f424
commit
b633939d4e
10 changed files with 326 additions and 310 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
@ -11,14 +11,15 @@ import FaturaList from '@/components/FaturaList';
|
||||||
import { useFaturas, FaturaFormData } from '@/hooks/useFaturas';
|
import { useFaturas, FaturaFormData } from '@/hooks/useFaturas';
|
||||||
import { RoleGuard } from '@/components/auth/RoleGuard';
|
import { RoleGuard } from '@/components/auth/RoleGuard';
|
||||||
import { UserRole } from '@/types/auth';
|
import { UserRole } from '@/types/auth';
|
||||||
|
import { FaturaDocument } from '@/types/legacyEntities';
|
||||||
|
|
||||||
const GestaoFaturas = () => {
|
const GestaoFaturas = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [user, setUser] = useState<Models.User<Models.Preferences> | null>(null);
|
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 [activeTab, setActiveTab] = useState<'lista' | 'cadastro'>('lista');
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
faturas,
|
faturas,
|
||||||
loading,
|
loading,
|
||||||
|
|
@ -30,21 +31,19 @@ const GestaoFaturas = () => {
|
||||||
cadastrarFatura,
|
cadastrarFatura,
|
||||||
atualizarFatura,
|
atualizarFatura,
|
||||||
deletarFatura,
|
deletarFatura,
|
||||||
setCurrentPage
|
|
||||||
} = useFaturas();
|
} = useFaturas();
|
||||||
|
|
||||||
// 🔠Verificar autenticação
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const initializeUser = async () => {
|
const initializeUser = async () => {
|
||||||
try {
|
try {
|
||||||
const currentUser = await account.get();
|
const currentUser = await account.get();
|
||||||
setUser(currentUser);
|
setUser(currentUser);
|
||||||
|
|
||||||
if (activeTab === 'lista') {
|
if (activeTab === 'lista') {
|
||||||
await listarFaturas();
|
await listarFaturas();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('⌠Usuário não autenticado:', error);
|
console.error('Usuário não autenticado:', error);
|
||||||
router.push('/');
|
router.push('/');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -60,16 +59,16 @@ const GestaoFaturas = () => {
|
||||||
setActiveTab('lista');
|
setActiveTab('lista');
|
||||||
}
|
}
|
||||||
return success;
|
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);
|
setEditing(fatura);
|
||||||
setActiveTab('cadastro');
|
setActiveTab('cadastro');
|
||||||
};
|
};
|
||||||
|
|
@ -80,33 +79,40 @@ const GestaoFaturas = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePrevPage = async () => {
|
const handlePrevPage = async () => {
|
||||||
if (currentPage > 1) {
|
if (currentPage <= 1) {
|
||||||
if (searchTerm.trim()) {
|
return;
|
||||||
await buscarFaturas(searchTerm, currentPage - 1);
|
|
||||||
} else {
|
|
||||||
await listarFaturas(currentPage - 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (searchTerm.trim()) {
|
||||||
|
await buscarFaturas(searchTerm, currentPage - 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await listarFaturas(currentPage - 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSearch = async (term: string) => {
|
const handleSearch = async (term: string) => {
|
||||||
setSearchTerm(term);
|
setSearchTerm(term);
|
||||||
if (term.trim()) {
|
if (term.trim()) {
|
||||||
await buscarFaturas(term, 1);
|
await buscarFaturas(term, 1);
|
||||||
} else {
|
return;
|
||||||
await listarFaturas(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await listarFaturas(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleNextPage = async () => {
|
const handleNextPage = async () => {
|
||||||
const totalPages = Math.ceil(totalFaturas / 10);
|
const totalPages = Math.ceil(totalFaturas / 10);
|
||||||
if (currentPage < totalPages) {
|
if (currentPage >= totalPages) {
|
||||||
if (searchTerm.trim()) {
|
return;
|
||||||
await buscarFaturas(searchTerm, currentPage + 1);
|
|
||||||
} else {
|
|
||||||
await listarFaturas(currentPage + 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (searchTerm.trim()) {
|
||||||
|
await buscarFaturas(searchTerm, currentPage + 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await listarFaturas(currentPage + 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
|
|
@ -114,7 +120,7 @@ const GestaoFaturas = () => {
|
||||||
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
|
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
|
||||||
<div className="text-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>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -123,75 +129,73 @@ const GestaoFaturas = () => {
|
||||||
return (
|
return (
|
||||||
<RoleGuard allowedRoles={[UserRole.ADMIN, UserRole.ADMIN]}>
|
<RoleGuard allowedRoles={[UserRole.ADMIN, UserRole.ADMIN]}>
|
||||||
<div className="min-h-screen bg-gray-50">
|
<div className="min-h-screen bg-gray-50">
|
||||||
<Header
|
<Header
|
||||||
user={user}
|
user={user}
|
||||||
title="Gestão de Faturas"
|
title="Gestão de Faturas"
|
||||||
subtitle="Gerencie as faturas da plataforma SaveInMed"
|
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 */}
|
<main className="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
|
||||||
{activeTab === 'lista' && (
|
<div className="mb-6">
|
||||||
<FaturaList
|
<nav className="flex space-x-8 bg-white p-4 rounded-lg shadow-sm">
|
||||||
faturas={faturas}
|
<button
|
||||||
loading={loading}
|
onClick={() => {
|
||||||
error={error}
|
setActiveTab('lista');
|
||||||
totalFaturas={totalFaturas}
|
setEditing(null);
|
||||||
currentPage={currentPage}
|
}}
|
||||||
pageSize={10}
|
className={`py-2 px-4 border-b-2 font-medium text-sm transition-colors cursor-pointer rounded-t-md ${
|
||||||
onEdit={handleEdit}
|
activeTab === 'lista'
|
||||||
onDelete={deletarFatura}
|
? 'border-blue-500 text-blue-600 bg-blue-50'
|
||||||
onRefresh={() =>
|
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 hover:bg-gray-50'
|
||||||
searchTerm.trim()
|
}`}
|
||||||
? buscarFaturas(searchTerm, currentPage)
|
>
|
||||||
: listarFaturas(currentPage)
|
<FileSpreadsheet className="w-4 h-4 inline-block mr-2" /> Listar Faturas
|
||||||
}
|
</button>
|
||||||
onPrevPage={handlePrevPage}
|
<button
|
||||||
onNextPage={handleNextPage}
|
onClick={() => {
|
||||||
onSearch={handleSearch}
|
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' && (
|
{activeTab === 'lista' && (
|
||||||
<FaturaForm
|
<FaturaList
|
||||||
onSubmit={handleFormSubmit}
|
faturas={faturas}
|
||||||
onCancel={editing ? handleCancelEdit : undefined}
|
loading={loading}
|
||||||
initialData={editing}
|
error={error}
|
||||||
loading={loading}
|
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>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</RoleGuard>
|
</RoleGuard>
|
||||||
|
|
@ -199,4 +203,3 @@ const GestaoFaturas = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default GestaoFaturas;
|
export default GestaoFaturas;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,67 +1,66 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Models } from '@/lib/appwrite';
|
|
||||||
import { CategoriaFormData } from '@/hooks/useCategorias';
|
import { CategoriaFormData } from '@/hooks/useCategorias';
|
||||||
import { Plus } from 'lucide-react';
|
import { Plus } from 'lucide-react';
|
||||||
|
import { CategoriaDocument } from '@/types/legacyEntities';
|
||||||
|
|
||||||
interface CategoriaFormProps {
|
interface CategoriaFormProps {
|
||||||
onSubmit: (data: CategoriaFormData) => Promise<boolean>;
|
onSubmit: (data: CategoriaFormData) => Promise<boolean>;
|
||||||
onCancel?: () => void;
|
onCancel?: () => void;
|
||||||
initialData?: Models.Document | null;
|
initialData?: CategoriaDocument | null;
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
type CategoriaDocument = Models.Document & {
|
|
||||||
nome?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const CategoriaForm: React.FC<CategoriaFormProps> = ({
|
const CategoriaForm: React.FC<CategoriaFormProps> = ({
|
||||||
onSubmit,
|
onSubmit,
|
||||||
onCancel,
|
onCancel,
|
||||||
initialData,
|
initialData,
|
||||||
loading = false
|
loading = false,
|
||||||
}) => {
|
}) => {
|
||||||
const [formData, setFormData] = useState<CategoriaFormData>({
|
const [formData, setFormData] = useState<CategoriaFormData>({
|
||||||
nome: ''
|
nome: '',
|
||||||
});
|
});
|
||||||
const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null);
|
const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (initialData) {
|
if (initialData) {
|
||||||
const document = initialData as CategoriaDocument;
|
setFormData({ nome: initialData.nome || '' });
|
||||||
setFormData({ nome: document.nome || '' });
|
return;
|
||||||
} else {
|
|
||||||
setFormData({ nome: '' });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setFormData({ nome: '' });
|
||||||
}, [initialData]);
|
}, [initialData]);
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const success = await onSubmit(formData);
|
const success = await onSubmit(formData);
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
setMessage({
|
setMessage({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
text: initialData ? '🔄 Categoria atualizada com sucesso!' : '🎉 Categoria cadastrada com sucesso!'
|
text: initialData ? 'Categoria atualizada com sucesso!' : 'Categoria cadastrada com sucesso!',
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!initialData) {
|
if (!initialData) {
|
||||||
setFormData({ nome: '' });
|
setFormData({ nome: '' });
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(() => setMessage(null), 3000);
|
setTimeout(() => setMessage(null), 3000);
|
||||||
} else {
|
return;
|
||||||
setMessage({
|
|
||||||
type: 'error',
|
|
||||||
text: initialData ? 'Erro ao atualizar categoria' : 'Erro ao cadastrar categoria'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setMessage({
|
||||||
|
type: 'error',
|
||||||
|
text: initialData ? 'Erro ao atualizar categoria' : 'Erro ao cadastrar categoria',
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const { name, value } = e.target;
|
const { name, value } = e.target;
|
||||||
setFormData(prev => ({ ...prev, [name]: value }));
|
setFormData(prev => ({ ...prev, [name]: value }));
|
||||||
if (message) setMessage(null);
|
if (message) {
|
||||||
|
setMessage(null);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
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</>)}
|
{loading ? 'Processando...' : (initialData ? 'Atualizar' : <><Plus className="w-4 h-4 inline-block mr-1" /> Cadastrar</>)}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{onCancel && (
|
{onCancel && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|
@ -127,4 +126,3 @@ const CategoriaForm: React.FC<CategoriaFormProps> = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CategoriaForm;
|
export default CategoriaForm;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Models } from '@/lib/appwrite';
|
|
||||||
import SearchBar from './SearchBar';
|
import SearchBar from './SearchBar';
|
||||||
import DataTable, { Column } from './DataTable';
|
import DataTable, { Column } from './DataTable';
|
||||||
import Pagination from './Pagination';
|
import Pagination from './Pagination';
|
||||||
import TableActions from './TableActions';
|
import TableActions from './TableActions';
|
||||||
|
import { CategoriaDocument } from '@/types/legacyEntities';
|
||||||
|
|
||||||
interface CategoriaListProps {
|
interface CategoriaListProps {
|
||||||
categorias: Models.Document[];
|
categorias: CategoriaDocument[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
isChangingPage?: boolean;
|
isChangingPage?: boolean;
|
||||||
error: string | null;
|
error: string | null;
|
||||||
totalCategorias: number;
|
totalCategorias: number;
|
||||||
currentPage: number;
|
currentPage: number;
|
||||||
pageSize: number;
|
pageSize: number;
|
||||||
onEdit: (lab: Models.Document) => void;
|
onEdit: (categoria: CategoriaDocument) => void;
|
||||||
onDelete: (id: string) => Promise<boolean>;
|
onDelete: (id: string) => Promise<boolean>;
|
||||||
onPrevPage: () => void;
|
onPrevPage: () => void;
|
||||||
onNextPage: () => void;
|
onNextPage: () => void;
|
||||||
|
|
@ -32,7 +32,7 @@ const CategoriaList: React.FC<CategoriaListProps> = ({
|
||||||
onDelete,
|
onDelete,
|
||||||
onPrevPage,
|
onPrevPage,
|
||||||
onNextPage,
|
onNextPage,
|
||||||
onSearch
|
onSearch,
|
||||||
}) => {
|
}) => {
|
||||||
const totalPages = Math.ceil(totalCategorias / pageSize);
|
const totalPages = Math.ceil(totalCategorias / pageSize);
|
||||||
const startItem = (currentPage - 1) * pageSize + 1;
|
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: 'nome', header: 'Nome' },
|
||||||
{ key: '$id', header: 'ID' },
|
{ 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 (
|
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="bg-blue-50 border border-blue-200 rounded-md p-3 mb-4">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-blue-600 mr-2"></div>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -91,10 +95,10 @@ const CategoriaList: React.FC<CategoriaListProps> = ({
|
||||||
<DataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={categorias}
|
data={categorias}
|
||||||
actions={(lab) => (
|
actions={categoria => (
|
||||||
<TableActions
|
<TableActions
|
||||||
onEdit={() => onEdit(lab)}
|
onEdit={() => onEdit(categoria)}
|
||||||
onDelete={() => handleDelete(lab.$id)}
|
onDelete={() => handleDelete(categoria.$id)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
@ -122,4 +126,4 @@ const CategoriaList: React.FC<CategoriaListProps> = ({
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CategoriaList;
|
export default CategoriaList;
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,15 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Models } from '@/lib/appwrite';
|
|
||||||
import { FaturaFormData } from '@/hooks/useFaturas';
|
import { FaturaFormData } from '@/hooks/useFaturas';
|
||||||
import { Plus } from 'lucide-react';
|
import { Plus } from 'lucide-react';
|
||||||
|
import { FaturaDocument } from '@/types/legacyEntities';
|
||||||
|
|
||||||
interface FaturaFormProps {
|
interface FaturaFormProps {
|
||||||
onSubmit: (data: FaturaFormData) => Promise<boolean>;
|
onSubmit: (data: FaturaFormData) => Promise<boolean>;
|
||||||
onCancel?: () => void;
|
onCancel?: () => void;
|
||||||
initialData?: Models.Document | null;
|
initialData?: FaturaDocument | null;
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
type FaturaDocument = Models.Document & {
|
|
||||||
nome?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const FaturaForm: React.FC<FaturaFormProps> = ({
|
const FaturaForm: React.FC<FaturaFormProps> = ({
|
||||||
onSubmit,
|
onSubmit,
|
||||||
onCancel,
|
onCancel,
|
||||||
|
|
@ -25,41 +21,45 @@ const FaturaForm: React.FC<FaturaFormProps> = ({
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (initialData) {
|
if (initialData) {
|
||||||
const document = initialData as FaturaDocument;
|
setFormData({ nome: initialData.nome || '' });
|
||||||
setFormData({ nome: document.nome || '' });
|
return;
|
||||||
} else {
|
|
||||||
setFormData({ nome: '' });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setFormData({ nome: '' });
|
||||||
}, [initialData]);
|
}, [initialData]);
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const success = await onSubmit(formData);
|
const success = await onSubmit(formData);
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
setMessage({
|
setMessage({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
text: initialData ? '🔄 Fatura atualizada com sucesso!' : '🎉 Fatura cadastrada com sucesso!'
|
text: initialData ? 'Fatura atualizada com sucesso!' : 'Fatura cadastrada com sucesso!',
|
||||||
});
|
});
|
||||||
if (!initialData) {
|
if (!initialData) {
|
||||||
setFormData({ nome: '' });
|
setFormData({ nome: '' });
|
||||||
}
|
}
|
||||||
setTimeout(() => setMessage(null), 3000);
|
setTimeout(() => setMessage(null), 3000);
|
||||||
} else {
|
return;
|
||||||
setMessage({ type: 'error', text: initialData ? 'Erro ao atualizar fatura' : 'Erro ao cadastrar fatura' });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setMessage({ type: 'error', text: initialData ? 'Erro ao atualizar fatura' : 'Erro ao cadastrar fatura' });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const { name, value } = e.target;
|
const { name, value } = e.target;
|
||||||
setFormData(prev => ({ ...prev, [name]: value }));
|
setFormData(prev => ({ ...prev, [name]: value }));
|
||||||
if (message) setMessage(null);
|
if (message) {
|
||||||
|
setMessage(null);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-white rounded-lg shadow-md p-6">
|
<div className="bg-white rounded-lg shadow-md p-6">
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<h2 className="text-2xl font-bold text-gray-900 mb-2">
|
<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>
|
</h2>
|
||||||
<p className="text-gray-600">
|
<p className="text-gray-600">
|
||||||
{initialData ? 'Atualize os dados da fatura.' : 'Cadastre uma nova fatura na plataforma SaveInMed.'}
|
{initialData ? 'Atualize os dados da fatura.' : 'Cadastre uma nova fatura na plataforma SaveInMed.'}
|
||||||
|
|
@ -95,7 +95,7 @@ const FaturaForm: React.FC<FaturaFormProps> = ({
|
||||||
disabled={loading}
|
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"
|
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>
|
</button>
|
||||||
{onCancel && (
|
{onCancel && (
|
||||||
<button
|
<button
|
||||||
|
|
@ -104,7 +104,7 @@ const FaturaForm: React.FC<FaturaFormProps> = ({
|
||||||
disabled={loading}
|
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"
|
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>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -114,5 +114,3 @@ const FaturaForm: React.FC<FaturaFormProps> = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
export default FaturaForm;
|
export default FaturaForm;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,20 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Models } from '@/lib/appwrite';
|
|
||||||
import SearchBar from './SearchBar';
|
import SearchBar from './SearchBar';
|
||||||
import RefreshButton from './RefreshButton';
|
import RefreshButton from './RefreshButton';
|
||||||
import ListHeader from './ListHeader';
|
import ListHeader from './ListHeader';
|
||||||
import DataTable, { Column } from './DataTable';
|
import DataTable, { Column } from './DataTable';
|
||||||
import Pagination from './Pagination';
|
import Pagination from './Pagination';
|
||||||
import TableActions from './TableActions';
|
import TableActions from './TableActions';
|
||||||
|
import { FaturaDocument } from '@/types/legacyEntities';
|
||||||
|
|
||||||
interface FaturaListProps {
|
interface FaturaListProps {
|
||||||
faturas: Models.Document[];
|
faturas: FaturaDocument[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
error: string | null;
|
error: string | null;
|
||||||
totalFaturas: number;
|
totalFaturas: number;
|
||||||
currentPage: number;
|
currentPage: number;
|
||||||
pageSize: number;
|
pageSize: number;
|
||||||
onEdit: (fatura: Models.Document) => void;
|
onEdit: (fatura: FaturaDocument) => void;
|
||||||
onDelete: (id: string) => Promise<boolean>;
|
onDelete: (id: string) => Promise<boolean>;
|
||||||
onRefresh: () => void;
|
onRefresh: () => void;
|
||||||
onPrevPage: () => void;
|
onPrevPage: () => void;
|
||||||
|
|
@ -34,7 +34,7 @@ const FaturaList: React.FC<FaturaListProps> = ({
|
||||||
onRefresh,
|
onRefresh,
|
||||||
onPrevPage,
|
onPrevPage,
|
||||||
onNextPage,
|
onNextPage,
|
||||||
onSearch
|
onSearch,
|
||||||
}) => {
|
}) => {
|
||||||
const totalPages = Math.ceil(totalFaturas / pageSize);
|
const totalPages = Math.ceil(totalFaturas / pageSize);
|
||||||
const startItem = (currentPage - 1) * pageSize + 1;
|
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: 'nome', header: 'Nome' },
|
||||||
{ key: '$id', header: 'ID' },
|
{ 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 (
|
return (
|
||||||
|
|
@ -85,7 +89,7 @@ const FaturaList: React.FC<FaturaListProps> = ({
|
||||||
<DataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={faturas}
|
data={faturas}
|
||||||
actions={(fatura) => (
|
actions={fatura => (
|
||||||
<TableActions
|
<TableActions
|
||||||
onEdit={() => onEdit(fatura)}
|
onEdit={() => onEdit(fatura)}
|
||||||
onDelete={() => handleDelete(fatura.$id)}
|
onDelete={() => handleDelete(fatura.$id)}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import { useState, useCallback, useRef, useEffect } from 'react';
|
import { useState, useCallback, useRef, useEffect } from 'react';
|
||||||
import { Models } from '@/lib/appwrite';
|
|
||||||
import { categoriaService } from '@/services/categoriaService';
|
import { categoriaService } from '@/services/categoriaService';
|
||||||
|
import { CategoriaDocument } from '@/types/legacyEntities';
|
||||||
|
|
||||||
export interface CategoriaFormData {
|
export interface CategoriaFormData {
|
||||||
nome: string;
|
nome: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UseCategoriasReturn {
|
export interface UseCategoriasReturn {
|
||||||
categorias: Models.Document[];
|
categorias: CategoriaDocument[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
isChangingPage: boolean;
|
isChangingPage: boolean;
|
||||||
isCreating: boolean;
|
isCreating: boolean;
|
||||||
|
|
@ -27,7 +27,7 @@ export interface UseCategoriasReturn {
|
||||||
const PAGE_SIZE = 10;
|
const PAGE_SIZE = 10;
|
||||||
|
|
||||||
export const useCategorias = (): UseCategoriasReturn => {
|
export const useCategorias = (): UseCategoriasReturn => {
|
||||||
const [categorias, setCategorias] = useState<Models.Document[]>([]);
|
const [categorias, setCategorias] = useState<CategoriaDocument[]>([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [totalCategorias, setTotalCategorias] = useState(0);
|
const [totalCategorias, setTotalCategorias] = useState(0);
|
||||||
|
|
@ -37,9 +37,7 @@ export const useCategorias = (): UseCategoriasReturn => {
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
const isInitialMount = useRef(true);
|
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);
|
setIsChangingPage(true);
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
@ -50,53 +48,47 @@ export const useCategorias = (): UseCategoriasReturn => {
|
||||||
: await categoriaService.listar(page, PAGE_SIZE);
|
: await categoriaService.listar(page, PAGE_SIZE);
|
||||||
|
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
setCategorias((response.documents || []) as Models.Document[]);
|
setCategorias(response.documents || []);
|
||||||
setTotalCategorias(response.total || 0);
|
setTotalCategorias(response.total || 0);
|
||||||
setCurrentPage(page);
|
setCurrentPage(page);
|
||||||
setSearchTerm(search);
|
setSearchTerm(search);
|
||||||
} else {
|
return;
|
||||||
setError(response.error || 'Erro ao carregar categorias');
|
|
||||||
}
|
}
|
||||||
} 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 {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setIsChangingPage(false);
|
setIsChangingPage(false);
|
||||||
}
|
}
|
||||||
}, [currentPage, searchTerm]);
|
}, [currentPage, searchTerm]);
|
||||||
|
|
||||||
const buscarCategorias = useCallback(
|
const buscarCategorias = useCallback(async (nome: string, page = 1) => {
|
||||||
async (nome: string, page = 1) => {
|
setIsChangingPage(true);
|
||||||
setIsChangingPage(true);
|
setLoading(true);
|
||||||
setLoading(true);
|
setError(null);
|
||||||
setError(null);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await categoriaService.buscarPorNome(
|
const response = await categoriaService.buscarPorNome(nome, page, PAGE_SIZE);
|
||||||
nome,
|
|
||||||
page,
|
|
||||||
PAGE_SIZE
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
setCategorias((response.documents || []) as Models.Document[]);
|
setCategorias(response.documents || []);
|
||||||
setTotalCategorias(response.total || 0);
|
setTotalCategorias(response.total || 0);
|
||||||
setCurrentPage(page);
|
setCurrentPage(page);
|
||||||
setSearchTerm(nome);
|
setSearchTerm(nome);
|
||||||
} else {
|
return;
|
||||||
setError(response.error || 'Erro ao buscar categorias');
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
setError('Erro de conexão ao buscar categorias');
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
setIsChangingPage(false);
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
// 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> => {
|
const cadastrarCategoria = useCallback(async (formData: CategoriaFormData): Promise<boolean> => {
|
||||||
setIsCreating(true);
|
setIsCreating(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
@ -107,12 +99,12 @@ export const useCategorias = (): UseCategoriasReturn => {
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
await listarCategorias(1, searchTerm);
|
await listarCategorias(1, searchTerm);
|
||||||
return true;
|
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;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
setIsCreating(false);
|
setIsCreating(false);
|
||||||
|
|
@ -129,17 +121,17 @@ export const useCategorias = (): UseCategoriasReturn => {
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
await listarCategorias(currentPage, searchTerm);
|
await listarCategorias(currentPage, searchTerm);
|
||||||
return true;
|
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;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [currentPage, listarCategorias]);
|
}, [currentPage, listarCategorias, searchTerm]);
|
||||||
|
|
||||||
const deletarCategoria = useCallback(async (id: string): Promise<boolean> => {
|
const deletarCategoria = useCallback(async (id: string): Promise<boolean> => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
@ -151,17 +143,17 @@ export const useCategorias = (): UseCategoriasReturn => {
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
await listarCategorias(currentPage, searchTerm);
|
await listarCategorias(currentPage, searchTerm);
|
||||||
return true;
|
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;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [currentPage, listarCategorias]);
|
}, [currentPage, listarCategorias, searchTerm]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isInitialMount.current) {
|
if (isInitialMount.current) {
|
||||||
|
|
@ -171,9 +163,10 @@ export const useCategorias = (): UseCategoriasReturn => {
|
||||||
|
|
||||||
if (searchTerm.trim()) {
|
if (searchTerm.trim()) {
|
||||||
buscarCategorias(searchTerm, currentPage);
|
buscarCategorias(searchTerm, currentPage);
|
||||||
} else {
|
return;
|
||||||
listarCategorias(currentPage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
listarCategorias(currentPage);
|
||||||
}, [currentPage, searchTerm, listarCategorias, buscarCategorias]);
|
}, [currentPage, searchTerm, listarCategorias, buscarCategorias]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -191,7 +184,6 @@ export const useCategorias = (): UseCategoriasReturn => {
|
||||||
deletarCategoria,
|
deletarCategoria,
|
||||||
setCurrentPage,
|
setCurrentPage,
|
||||||
searchTerm,
|
searchTerm,
|
||||||
setSearchTerm
|
setSearchTerm,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
import { Models } from '@/lib/appwrite';
|
|
||||||
import { faturaService } from '@/services/faturaService';
|
import { faturaService } from '@/services/faturaService';
|
||||||
|
import { FaturaDocument } from '@/types/legacyEntities';
|
||||||
|
|
||||||
export interface FaturaFormData {
|
export interface FaturaFormData {
|
||||||
nome: string;
|
nome: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UseFaturasReturn {
|
export interface UseFaturasReturn {
|
||||||
faturas: Models.Document[];
|
faturas: FaturaDocument[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
error: string | null;
|
error: string | null;
|
||||||
totalFaturas: number;
|
totalFaturas: number;
|
||||||
|
|
@ -23,13 +23,12 @@ export interface UseFaturasReturn {
|
||||||
const PAGE_SIZE = 10;
|
const PAGE_SIZE = 10;
|
||||||
|
|
||||||
export const useFaturas = (): UseFaturasReturn => {
|
export const useFaturas = (): UseFaturasReturn => {
|
||||||
const [faturas, setFaturas] = useState<Models.Document[]>([]);
|
const [faturas, setFaturas] = useState<FaturaDocument[]>([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [totalFaturas, setTotalFaturas] = useState(0);
|
const [totalFaturas, setTotalFaturas] = useState(0);
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
|
|
||||||
// Listagem usando SDK do Appwrite
|
|
||||||
const listarFaturas = useCallback(async (page = currentPage) => {
|
const listarFaturas = useCallback(async (page = currentPage) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
@ -38,48 +37,42 @@ export const useFaturas = (): UseFaturasReturn => {
|
||||||
const response = await faturaService.listar(page, PAGE_SIZE);
|
const response = await faturaService.listar(page, PAGE_SIZE);
|
||||||
|
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
setFaturas((response.documents || []) as Models.Document[]);
|
setFaturas(response.documents || []);
|
||||||
setTotalFaturas(response.total || 0);
|
setTotalFaturas(response.total || 0);
|
||||||
setCurrentPage(page);
|
setCurrentPage(page);
|
||||||
} else {
|
return;
|
||||||
setError(response.error || 'Erro ao carregar faturas');
|
|
||||||
}
|
}
|
||||||
} 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 {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [currentPage]);
|
}, [currentPage]);
|
||||||
|
|
||||||
const buscarFaturas = useCallback(
|
const buscarFaturas = useCallback(async (nome: string, page = 1) => {
|
||||||
async (nome: string, page = 1) => {
|
setLoading(true);
|
||||||
setLoading(true);
|
setError(null);
|
||||||
setError(null);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await faturaService.buscarPorNome(
|
const response = await faturaService.buscarPorNome(nome, page, PAGE_SIZE);
|
||||||
nome,
|
|
||||||
page,
|
|
||||||
PAGE_SIZE
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
setFaturas((response.documents || []) as Models.Document[]);
|
setFaturas(response.documents || []);
|
||||||
setTotalFaturas(response.total || 0);
|
setTotalFaturas(response.total || 0);
|
||||||
setCurrentPage(page);
|
setCurrentPage(page);
|
||||||
} else {
|
return;
|
||||||
setError(response.error || 'Erro ao buscar faturas');
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
setError('Erro de conexão ao buscar faturas');
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
// 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> => {
|
const cadastrarFatura = useCallback(async (formData: FaturaFormData): Promise<boolean> => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
@ -90,12 +83,12 @@ export const useFaturas = (): UseFaturasReturn => {
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
await listarFaturas(currentPage);
|
await listarFaturas(currentPage);
|
||||||
return true;
|
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;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|
@ -112,12 +105,12 @@ export const useFaturas = (): UseFaturasReturn => {
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
await listarFaturas(currentPage);
|
await listarFaturas(currentPage);
|
||||||
return true;
|
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;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|
@ -134,12 +127,12 @@ export const useFaturas = (): UseFaturasReturn => {
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
await listarFaturas(currentPage);
|
await listarFaturas(currentPage);
|
||||||
return true;
|
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;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|
@ -160,5 +153,3 @@ export const useFaturas = (): UseFaturasReturn => {
|
||||||
setCurrentPage,
|
setCurrentPage,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,29 @@
|
||||||
// @ts-nocheck
|
import { CategoriaDocument, ServiceResponse } from '@/types/legacyEntities';
|
||||||
interface CategoriaData {
|
|
||||||
|
export interface CategoriaData {
|
||||||
id?: string;
|
id?: string;
|
||||||
nome?: string;
|
nome?: string;
|
||||||
[key: string]: any;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ServiceResponse<T = unknown> {
|
const emptyList = (): ServiceResponse<CategoriaDocument> => ({
|
||||||
success: boolean;
|
success: true,
|
||||||
data?: T;
|
data: null,
|
||||||
documents?: T[];
|
documents: [],
|
||||||
total?: number;
|
total: 0,
|
||||||
error?: string;
|
});
|
||||||
message?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emptyList = <T,>(): ServiceResponse<T> => ({ success: true, data: [], documents: [], total: 0 });
|
const removed = (message = 'Service removido'): ServiceResponse<CategoriaDocument> => ({
|
||||||
const removed = (message = 'Service removido'): ServiceResponse => ({ success: false, error: message, message });
|
success: false,
|
||||||
|
error: message,
|
||||||
|
message,
|
||||||
|
});
|
||||||
|
|
||||||
export const categoriaService = {
|
export const categoriaService = {
|
||||||
listarTodas: async () => emptyList<CategoriaData>(),
|
listarTodas: async () => emptyList(),
|
||||||
listar: async (_page = 1, _limit = 10) => emptyList<CategoriaData>(),
|
listar: async (_page = 1, _limit = 10) => emptyList(),
|
||||||
buscarPorNome: async (_nome: string, _page = 1, _limit = 10) => emptyList<CategoriaData>(),
|
buscarPorNome: async (_nome: string, _page = 1, _limit = 10) => emptyList(),
|
||||||
buscarPorId: async (_id?: string) => ({ success: true, data: null as CategoriaData | null }),
|
buscarPorId: async (_id?: string): Promise<ServiceResponse<CategoriaDocument>> => ({ success: true, data: null }),
|
||||||
criar: async (_data?: CategoriaData) => removed(),
|
criar: async (_data?: CategoriaData) => removed(),
|
||||||
atualizar: async (_id?: string, _data?: Partial<CategoriaData>) => removed(),
|
atualizar: async (_id?: string, _data?: Partial<CategoriaData>) => removed(),
|
||||||
deletar: async (_id?: string) => removed(),
|
deletar: async (_id?: string) => removed(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,30 @@
|
||||||
interface FaturaData {
|
import { FaturaDocument, ServiceResponse } from '@/types/legacyEntities';
|
||||||
|
|
||||||
|
export interface FaturaData {
|
||||||
id?: string;
|
id?: string;
|
||||||
[key: string]: any;
|
nome?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ServiceResponse<T = unknown> {
|
const emptyList = (): ServiceResponse<FaturaDocument> => ({
|
||||||
success: boolean;
|
success: true,
|
||||||
data?: T;
|
data: null,
|
||||||
documents?: T[];
|
documents: [],
|
||||||
total?: number;
|
total: 0,
|
||||||
error?: string;
|
});
|
||||||
message?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emptyList = <T,>(): ServiceResponse<T> => ({ success: true, documents: [], total: 0 });
|
const removed = (message = 'Service removido'): ServiceResponse<FaturaDocument> => ({
|
||||||
const removed = (message = 'Service removido'): ServiceResponse => ({ success: false, error: message, message });
|
success: false,
|
||||||
|
error: message,
|
||||||
|
message,
|
||||||
|
});
|
||||||
|
|
||||||
export const faturaService = {
|
export const faturaService = {
|
||||||
listar: async (_page = 1, _limit = 10) => emptyList<FaturaData>(),
|
listar: async (_page = 1, _limit = 10) => emptyList(),
|
||||||
buscarPorNome: async (_nome: string, _page = 1, _limit = 10) => emptyList<FaturaData>(),
|
buscarPorNome: async (_nome: string, _page = 1, _limit = 10) => emptyList(),
|
||||||
criar: async (_data?: FaturaData) => removed(),
|
criar: async (_data?: FaturaData) => removed(),
|
||||||
criarFatura: async (_data?: FaturaData) => removed(),
|
criarFatura: async (_data?: FaturaData) => removed(),
|
||||||
buscarPorUserId: async () => emptyList<FaturaData>(),
|
buscarPorUserId: async () => emptyList(),
|
||||||
buscarPorId: async (_id?: string) => ({ success: true, data: null as FaturaData | null }),
|
buscarPorId: async (_id?: string): Promise<ServiceResponse<FaturaDocument>> => ({ success: true, data: null }),
|
||||||
atualizar: async (_id?: string, _data?: Partial<FaturaData>) => removed(),
|
atualizar: async (_id?: string, _data?: Partial<FaturaData>) => removed(),
|
||||||
deletar: async (_id?: string) => removed(),
|
deletar: async (_id?: string) => removed(),
|
||||||
atualizarFaturaParcial: async (_id?: string, _data?: Partial<FaturaData>) => removed(),
|
atualizarFaturaParcial: async (_id?: string, _data?: Partial<FaturaData>) => removed(),
|
||||||
|
|
|
||||||
23
frontend/src/types/legacyEntities.ts
Normal file
23
frontend/src/types/legacyEntities.ts
Normal 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;
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue