refactor(frontend): decouple catalog components and api routes from appwrite stub

This commit is contained in:
Tiago Yamamoto 2026-03-07 08:04:19 -06:00
parent 48a54a616e
commit a1eb0efc72
6 changed files with 213 additions and 334 deletions

View file

@ -1,56 +1,63 @@
// @ts-nocheck import { NextRequest, NextResponse } from 'next/server';
import { NextRequest, NextResponse } from "next/server"; import { Client, Databases, Query } from 'node-appwrite';
import { databases } from "@/lib/appwrite";
import { Query } from "@/lib/appwrite";
export const runtime = "edge"; export const runtime = 'edge';
const createDatabasesClient = () => {
const apiKey = process.env.APPWRITE_API_KEY;
const endpoint = process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT;
const projectId = process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID;
if (!apiKey || !endpoint || !projectId) {
throw new Error('Configuração do Appwrite ausente');
}
const client = new Client().setEndpoint(endpoint).setProject(projectId).setKey(apiKey);
return new Databases(client);
};
export async function GET(request: NextRequest) { export async function GET(request: NextRequest) {
console.log("🔍 [API] Debug registro usuario..."); console.log('[API] Debug registro usuario...');
try { try {
const { searchParams } = new URL(request.url); const { searchParams } = new URL(request.url);
const userId = searchParams.get("userId"); const userId = searchParams.get('userId');
if (!userId) { if (!userId) {
return NextResponse.json( return NextResponse.json(
{ success: false, error: "userId é obrigatório" }, { success: false, error: 'userId é obrigatório' },
{ status: 400 } { status: 400 }
); );
} }
const databaseId = process.env.NEXT_PUBLIC_APPWRITE_DATABASE_ID!; const databaseId = process.env.NEXT_PUBLIC_APPWRITE_DATABASE_ID!;
const collectionId = const collectionId = process.env.NEXT_PUBLIC_APPWRITE_COLLECTION_USUARIOS_ID!;
process.env.NEXT_PUBLIC_APPWRITE_COLLECTION_USUARIOS_ID!; const databases = createDatabasesClient();
console.log("🔍 [API] Buscando usuário com auth-id-appwrite:", userId); console.log('[API] Buscando usuário com auth-id-appwrite:', userId);
// Buscar usuário pelo auth-id-appwrite
const response = await databases.listDocuments(databaseId, collectionId, [ const response = await databases.listDocuments(databaseId, collectionId, [
Query.equal("auth-id-appwrite", userId), Query.equal('auth-id-appwrite', userId),
Query.limit(1), Query.limit(1),
]); ]);
if (response.documents.length === 0) { if (response.documents.length === 0) {
console.log("❌ [API] Usuário não encontrado"); console.log('[API] Usuário não encontrado');
return NextResponse.json({ return NextResponse.json({
success: true, success: true,
found: false, found: false,
message: "Usuário não encontrado na base de dados", message: 'Usuário não encontrado na base de dados',
}); });
} }
const usuario = response.documents[0]; const usuario = response.documents[0] as Record<string, any>;
console.log("✅ [API] Usuário encontrado:", usuario.$id);
// Análise detalhada dos dados
const analise = { const analise = {
id: usuario.$id, id: usuario.$id,
"auth-id-appwrite": usuario["auth-id-appwrite"], 'auth-id-appwrite': usuario['auth-id-appwrite'],
"nome-civil": { 'nome-civil': {
valor: usuario["nome-civil"], valor: usuario['nome-civil'],
preenchido: !!usuario["nome-civil"], preenchido: !!usuario['nome-civil'],
}, },
cpf: { cpf: {
valor: usuario.cpf, valor: usuario.cpf,
@ -64,9 +71,7 @@ export async function GET(request: NextRequest) {
valido: valido:
Array.isArray(usuario.enderecos) && Array.isArray(usuario.enderecos) &&
usuario.enderecos.length > 0 && usuario.enderecos.length > 0 &&
usuario.enderecos.some( usuario.enderecos.some((endereco: string) => endereco && endereco.trim() !== ''),
(endereco) => endereco && endereco.trim() !== ""
),
}, },
empresas_array: { empresas_array: {
valor: usuario.empresas_array, valor: usuario.empresas_array,
@ -80,29 +85,22 @@ export async function GET(request: NextRequest) {
}, },
}; };
const dadosFaltantes = []; const dadosFaltantes: string[] = [];
// Verificar cada campo if (!analise['nome-civil'].preenchido || !analise.cpf.preenchido) {
if (!analise["nome-civil"].preenchido || !analise.cpf.preenchido) { dadosFaltantes.push('Dados pessoais');
dadosFaltantes.push("Dados pessoais");
} }
if (!analise.enderecos.valido) { if (!analise.enderecos.valido) {
dadosFaltantes.push("Endereço"); dadosFaltantes.push('Endereço');
} }
if (!analise.empresas_array.valido) { if (!analise.empresas_array.valido) {
dadosFaltantes.push("Empresa"); dadosFaltantes.push('Empresa');
} }
const isCompleto = dadosFaltantes.length === 0; const isCompleto = dadosFaltantes.length === 0;
console.log("📋 [API] Análise completa:", {
isCompleto,
dadosFaltantes,
analise,
});
return NextResponse.json({ return NextResponse.json({
success: true, success: true,
found: true, found: true,
@ -112,11 +110,10 @@ export async function GET(request: NextRequest) {
dadosCompletos: usuario, dadosCompletos: usuario,
}); });
} catch (error) { } catch (error) {
console.error("❌ [API] Erro ao debugar registro:", error); console.error('[API] Erro ao debugar registro:', error);
return NextResponse.json( return NextResponse.json(
{ success: false, error: "Erro ao verificar registro" }, { success: false, error: 'Erro ao verificar registro' },
{ status: 500 } { status: 500 }
); );
} }
} }

View file

@ -1,85 +1,74 @@
// @ts-nocheck // @ts-nocheck
import { NextRequest, NextResponse } from "next/server"; import { NextRequest, NextResponse } from 'next/server';
import { databases, Query } from "@/lib/appwrite"; import { Client, Databases, Query } from 'node-appwrite';
// Runtime edge para melhor performance export const runtime = 'edge';
export const runtime = "edge";
const DATABASE_ID = process.env.NEXT_PUBLIC_APPWRITE_DATABASE_ID!; const DATABASE_ID = process.env.NEXT_PUBLIC_APPWRITE_DATABASE_ID!;
const COLLECTION_CATALOGO = const COLLECTION_CATALOGO = process.env.NEXT_PUBLIC_APPWRITE_COLLECTION_CATALOGO_PRODUTOS_ID!;
process.env.NEXT_PUBLIC_APPWRITE_COLLECTION_CATALOGO_PRODUTOS_ID!; const COLLECTION_CATEGORIAS = process.env.NEXT_PUBLIC_APPWRITE_COLLECTION_CATEGORIAS_ID!;
const COLLECTION_CATEGORIAS =
process.env.NEXT_PUBLIC_APPWRITE_COLLECTION_CATEGORIAS_ID!; const createDatabasesClient = () => {
const apiKey = process.env.APPWRITE_API_KEY;
const endpoint = process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT;
const projectId = process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID;
if (!apiKey || !endpoint || !projectId) {
throw new Error('Configuração do Appwrite ausente');
}
const client = new Client().setEndpoint(endpoint).setProject(projectId).setKey(apiKey);
return new Databases(client);
};
// Mapeamento de produtos para categorias
const mapeamentoCategorias: { [key: string]: string } = { const mapeamentoCategorias: { [key: string]: string } = {
dipirona: "RX", dipirona: 'RX',
cetaphil: "Dermo", cetaphil: 'Dermo',
sabonete: "Dermo", sabonete: 'Dermo',
dermo: "Dermo", dermo: 'Dermo',
vitamina: "Nutrição", vitamina: 'Nutrição',
suplemento: "Nutrição", suplemento: 'Nutrição',
generico: "Genérico", generico: 'Genérico',
diabetes: "Diabetes", diabetes: 'Diabetes',
insulina: "Diabetes", insulina: 'Diabetes',
medicamento: "RX", medicamento: 'RX',
remedio: "RX", remedio: 'RX',
}; };
export async function POST(request: NextRequest) { export async function POST(request: NextRequest) {
try { try {
console.log("🔗 [API] Iniciando relacionamento de categorias..."); const databases = createDatabasesClient();
// 1. Buscar todas as categorias
const categoriasResponse = await databases.listDocuments( const categoriasResponse = await databases.listDocuments(
DATABASE_ID, DATABASE_ID,
COLLECTION_CATEGORIAS, COLLECTION_CATEGORIAS,
[Query.limit(100)] [Query.limit(100)]
); );
console.log(
`📋 [API] Encontradas ${categoriasResponse.documents.length} categorias`
);
const categorias = categoriasResponse.documents; const categorias = categoriasResponse.documents;
const categoriasPorNome: { [key: string]: string } = {}; const categoriasPorNome: { [key: string]: string } = {};
categorias.forEach((cat) => { categorias.forEach((cat: any) => {
categoriasPorNome[cat.nome.toLowerCase()] = cat.$id; categoriasPorNome[String(cat.nome).toLowerCase()] = cat.$id;
}); });
console.log(
"📋 [API] Categorias disponíveis:",
Object.keys(categoriasPorNome)
);
// 2. Buscar todos os produtos do catálogo
const produtosResponse = await databases.listDocuments( const produtosResponse = await databases.listDocuments(
DATABASE_ID, DATABASE_ID,
COLLECTION_CATALOGO, COLLECTION_CATALOGO,
[Query.limit(100)] [Query.limit(100)]
); );
console.log(
`📦 [API] Encontrados ${produtosResponse.documents.length} produtos`
);
const produtos = produtosResponse.documents; const produtos = produtosResponse.documents;
const resultados = []; const resultados = [];
// 3. Para cada produto, determinar e atribuir categoria for (const produto of produtos as any[]) {
for (const produto of produtos) { const descricao = String(produto.descricao || '').toLowerCase();
const descricao = produto.descricao.toLowerCase();
let categoriaEncontrada = null; let categoriaEncontrada = null;
let nomeCategoria = ""; let nomeCategoria = '';
// Tentar encontrar categoria por palavra-chave for (const [palavra, nomeCategoriaMapeada] of Object.entries(mapeamentoCategorias)) {
for (const [palavra, nomeCategoriaMapeada] of Object.entries(
mapeamentoCategorias
)) {
if (descricao.includes(palavra)) { if (descricao.includes(palavra)) {
const categoriaId = const categoriaId = categoriasPorNome[nomeCategoriaMapeada.toLowerCase()];
categoriasPorNome[nomeCategoriaMapeada.toLowerCase()];
if (categoriaId) { if (categoriaId) {
categoriaEncontrada = categoriaId; categoriaEncontrada = categoriaId;
nomeCategoria = nomeCategoriaMapeada; nomeCategoria = nomeCategoriaMapeada;
@ -88,26 +77,17 @@ export async function POST(request: NextRequest) {
} }
} }
// Se não encontrou, usar RX como padrão
if (!categoriaEncontrada) { if (!categoriaEncontrada) {
categoriaEncontrada = categoriaEncontrada = categoriasPorNome['rx'] || categoriasPorNome['medicamento'];
categoriasPorNome["rx"] || categoriasPorNome["medicamento"]; nomeCategoria = 'RX';
nomeCategoria = "RX";
} }
console.log(
`📦 [API] Produto: ${produto.descricao} → Categoria: ${nomeCategoria}`
);
try { try {
// Atualizar produto com categoria
await databases.updateDocument( await databases.updateDocument(
DATABASE_ID, DATABASE_ID,
COLLECTION_CATALOGO, COLLECTION_CATALOGO,
produto.$id, produto.$id,
{ { categorias: categoriaEncontrada }
categorias: categoriaEncontrada,
}
); );
resultados.push({ resultados.push({
@ -116,63 +96,47 @@ export async function POST(request: NextRequest) {
categoria: nomeCategoria, categoria: nomeCategoria,
sucesso: true, sucesso: true,
}); });
console.log(
`✅ [API] Produto ${produto.$id} atualizado com categoria ${nomeCategoria}`
);
} catch (updateError) { } catch (updateError) {
console.error(
`❌ [API] Erro ao atualizar produto ${produto.$id}:`,
updateError
);
resultados.push({ resultados.push({
produtoId: produto.$id, produtoId: produto.$id,
descricao: produto.descricao, descricao: produto.descricao,
categoria: nomeCategoria, categoria: nomeCategoria,
sucesso: false, sucesso: false,
erro: erro: updateError instanceof Error ? updateError.message : String(updateError),
updateError instanceof Error
? updateError.message
: String(updateError),
}); });
} }
// Pequena pausa para não sobrecarregar
await new Promise((resolve) => setTimeout(resolve, 100)); await new Promise((resolve) => setTimeout(resolve, 100));
} }
console.log("✅ [API] Processo de relacionamento concluído!");
return NextResponse.json({ return NextResponse.json({
sucesso: true, sucesso: true,
mensagem: "Relacionamento de categorias concluído", mensagem: 'Relacionamento de categorias concluído',
totalProdutos: produtos.length, totalProdutos: produtos.length,
resultados: resultados, resultados,
categoriasDisponiveis: Object.keys(categoriasPorNome), categoriasDisponiveis: Object.keys(categoriasPorNome),
}); });
} catch (error) { } catch (error) {
console.error("❌ [API] Erro no relacionamento:", error); console.error('[API] Erro no relacionamento:', error);
return NextResponse.json( return NextResponse.json(
{ {
sucesso: false, sucesso: false,
erro: error instanceof Error ? error.message : String(error), erro: error instanceof Error ? error.message : String(error),
mensagem: "Erro ao relacionar categorias", mensagem: 'Erro ao relacionar categorias',
}, },
{ status: 500 } { status: 500 }
); );
} }
} }
export async function GET(request: NextRequest) { export async function GET(_request: NextRequest) {
return NextResponse.json({ return NextResponse.json({
mensagem: "Use POST para executar o relacionamento de categorias", mensagem: 'Use POST para executar o relacionamento de categorias',
instrucoes: [ instrucoes: [
"1. Faça uma requisição POST para esta rota", '1. Faça uma requisição POST para esta rota',
"2. O sistema irá relacionar automaticamente produtos com categorias", '2. O sistema irá relacionar automaticamente produtos com categorias',
"3. Após a execução, vá em /produtos para verificar", '3. Após a execução, vá em /produtos para verificar',
], ],
}); });
} }

View file

@ -1,35 +1,36 @@
import { NextRequest, NextResponse } from "next/server"; import { NextRequest, NextResponse } from 'next/server';
import { databases } from "@/lib/appwrite"; import { Client, Databases } from 'node-appwrite';
// Runtime edge para melhor performance export const runtime = 'edge';
export const runtime = "edge";
const DATABASE_ID = process.env.NEXT_PUBLIC_APPWRITE_DATABASE_ID!; const DATABASE_ID = process.env.NEXT_PUBLIC_APPWRITE_DATABASE_ID!;
const COLLECTION_CATALOGO = const COLLECTION_CATALOGO = process.env.NEXT_PUBLIC_APPWRITE_COLLECTION_CATALOGO_PRODUTOS_ID!;
process.env.NEXT_PUBLIC_APPWRITE_COLLECTION_CATALOGO_PRODUTOS_ID!;
const createDatabasesClient = () => {
const apiKey = process.env.APPWRITE_API_KEY;
const endpoint = process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT;
const projectId = process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID;
if (!apiKey || !endpoint || !projectId) {
throw new Error('Configuração do Appwrite ausente');
}
const client = new Client().setEndpoint(endpoint).setProject(projectId).setKey(apiKey);
return new Databases(client);
};
export async function POST(request: NextRequest) { export async function POST(request: NextRequest) {
try { try {
const { produtoId, categoriaId, subcategoriaId } = await request.json(); const { produtoId, categoriaId, subcategoriaId } = await request.json();
console.log("🔗 [API] Relacionando produto com categoria:", {
produtoId,
categoriaId,
subcategoriaId,
});
if (!produtoId?.trim()) { if (!produtoId?.trim()) {
return NextResponse.json( return NextResponse.json(
{ { sucesso: false, erro: 'ID do produto é obrigatório' },
sucesso: false,
erro: "ID do produto é obrigatório",
},
{ status: 400 } { status: 400 }
); );
} }
// Preparar dados para atualização const updateData: Record<string, string> = {};
const updateData: any = {};
if (categoriaId?.trim()) { if (categoriaId?.trim()) {
updateData.categoria = categoriaId.trim(); updateData.categoria = categoriaId.trim();
@ -41,15 +42,12 @@ export async function POST(request: NextRequest) {
if (Object.keys(updateData).length === 0) { if (Object.keys(updateData).length === 0) {
return NextResponse.json( return NextResponse.json(
{ { sucesso: false, erro: 'Pelo menos uma categoria ou subcategoria deve ser fornecida' },
sucesso: false,
erro: "Pelo menos uma categoria ou subcategoria deve ser fornecida",
},
{ status: 400 } { status: 400 }
); );
} }
// Atualizar produto no catálogo const databases = createDatabasesClient();
const produtoAtualizado = await databases.updateDocument( const produtoAtualizado = await databases.updateDocument(
DATABASE_ID, DATABASE_ID,
COLLECTION_CATALOGO, COLLECTION_CATALOGO,
@ -57,24 +55,19 @@ export async function POST(request: NextRequest) {
updateData updateData
); );
console.log(
"✅ [API] Produto relacionado com sucesso:",
produtoAtualizado.$id
);
return NextResponse.json({ return NextResponse.json({
sucesso: true, sucesso: true,
produto: produtoAtualizado, produto: produtoAtualizado,
mensagem: "Produto relacionado com categoria/subcategoria com sucesso!", mensagem: 'Produto relacionado com categoria/subcategoria com sucesso!',
}); });
} catch (error) { } catch (error) {
console.error("❌ [API] Erro ao relacionar produto:", error); console.error('[API] Erro ao relacionar produto:', error);
return NextResponse.json( return NextResponse.json(
{ {
sucesso: false, sucesso: false,
erro: error instanceof Error ? error.message : "Erro desconhecido", erro: error instanceof Error ? error.message : 'Erro desconhecido',
mensagem: "Erro ao relacionar produto com categoria", mensagem: 'Erro ao relacionar produto com categoria',
}, },
{ status: 500 } { status: 500 }
); );

View file

@ -1,11 +1,11 @@
// @ts-nocheck // @ts-nocheck
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Models } from '@/lib/appwrite';
import { ShoppingCartIcon, MinusIcon } from '@heroicons/react/24/outline'; import { ShoppingCartIcon, MinusIcon } from '@heroicons/react/24/outline';
import { useCatalogoProdutos } from '@/hooks/useCatalogoProdutos'; import { useCatalogoProdutos } from '@/hooks/useCatalogoProdutos';
import { CatalogoProdutoDocument } from '@/types/legacyEntities';
interface ItemCarrinho { interface ItemCarrinho {
produto: Models.Document; produto: CatalogoProdutoDocument;
quantidade: number; quantidade: number;
} }
@ -14,22 +14,22 @@ interface CatalogoComCarrinhoProps {
} }
const CatalogoComCarrinho: React.FC<CatalogoComCarrinhoProps> = ({ onAdicionarAoCarrinho }) => { const CatalogoComCarrinho: React.FC<CatalogoComCarrinhoProps> = ({ onAdicionarAoCarrinho }) => {
const { catalogoProdutos, loading, listarCatalogo, totalCatalogoProdutos, currentPage } = useCatalogoProdutos(); const { catalogoProdutos, loading, listarCatalogo, totalCatalogoProdutos } = useCatalogoProdutos();
const [carrinho, setCarrinho] = useState<ItemCarrinho[]>([]); const [carrinho, setCarrinho] = useState<ItemCarrinho[]>([]);
const [termoBusca, setTermoBusca] = useState(''); const [termoBusca, setTermoBusca] = useState('');
const [quantidades, setQuantidades] = useState<Record<string, number>>({}); const [quantidades, setQuantidades] = useState<Record<string, number>>({});
useEffect(() => { useEffect(() => {
listarCatalogo(1, termoBusca); listarCatalogo(1, termoBusca);
}, [termoBusca]); }, [termoBusca, listarCatalogo]);
const adicionarAoCarrinho = (produto: Models.Document) => { const adicionarAoCarrinho = (produto: CatalogoProdutoDocument) => {
const quantidade = quantidades[produto.$id] || 1; const quantidade = quantidades[produto.$id] || 1;
const itemExistente = carrinho.find(item => item.produto.$id === produto.$id); const itemExistente = carrinho.find(item => item.produto.$id === produto.$id);
if (itemExistente) { if (itemExistente) {
const novoCarrinho = carrinho.map(item => const novoCarrinho = carrinho.map(item =>
item.produto.$id === produto.$id item.produto.$id === produto.$id
? { ...item, quantidade: item.quantidade + quantidade } ? { ...item, quantidade: item.quantidade + quantidade }
: item : item
); );
@ -38,17 +38,14 @@ const CatalogoComCarrinho: React.FC<CatalogoComCarrinhoProps> = ({ onAdicionarAo
const novoItem: ItemCarrinho = { produto, quantidade }; const novoItem: ItemCarrinho = { produto, quantidade };
setCarrinho([...carrinho, novoItem]); setCarrinho([...carrinho, novoItem]);
} }
// Reset quantidade para 1 após adicionar
setQuantidades(prev => ({ ...prev, [produto.$id]: 1 })); setQuantidades(prev => ({ ...prev, [produto.$id]: 1 }));
if (onAdicionarAoCarrinho) { if (onAdicionarAoCarrinho) {
onAdicionarAoCarrinho({ produto, quantidade }); onAdicionarAoCarrinho({ produto, quantidade });
} }
}; };
const removerDoCarrinho = (produtoId: string) => { const removerDoCarrinho = (produtoId: string) => {
setCarrinho(carrinho.filter(item => item.produto.$id !== produtoId)); setCarrinho(carrinho.filter(item => item.produto.$id !== produtoId));
}; };
@ -60,12 +57,10 @@ const CatalogoComCarrinho: React.FC<CatalogoComCarrinhoProps> = ({ onAdicionarAo
}, 0); }, 0);
}; };
const formatarPreco = (preco: number) => { const formatarPreco = (preco: number) => new Intl.NumberFormat('pt-BR', {
return new Intl.NumberFormat('pt-BR', { style: 'currency',
style: 'currency', currency: 'BRL'
currency: 'BRL' }).format(preco);
}).format(preco);
};
if (loading) { if (loading) {
return ( return (
@ -77,7 +72,6 @@ const CatalogoComCarrinho: React.FC<CatalogoComCarrinhoProps> = ({ onAdicionarAo
return ( return (
<div className="space-y-6"> <div className="space-y-6">
{/* Barra de Busca */}
<div className="bg-white p-4 rounded-lg shadow"> <div className="bg-white p-4 rounded-lg shadow">
<div className="flex gap-4"> <div className="flex gap-4">
<input <input
@ -94,31 +88,26 @@ const CatalogoComCarrinho: React.FC<CatalogoComCarrinhoProps> = ({ onAdicionarAo
</div> </div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6"> <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Lista de Produtos */}
<div className="lg:col-span-2"> <div className="lg:col-span-2">
<div className="bg-white rounded-lg shadow"> <div className="bg-white rounded-lg shadow">
<div className="p-4 border-b border-gray-200"> <div className="p-4 border-b border-gray-200">
<h2 className="text-lg font-semibold text-gray-900">Catálogo de Produtos</h2> <h2 className="text-lg font-semibold text-gray-900">Catálogo de Produtos</h2>
<p className="text-sm text-gray-500">{totalCatalogoProdutos} produtos encontrados</p> <p className="text-sm text-gray-500">{totalCatalogoProdutos} produtos encontrados</p>
</div> </div>
<div className="divide-y divide-gray-200"> <div className="divide-y divide-gray-200">
{catalogoProdutos.map((produto) => { {catalogoProdutos.map((produto) => {
const preco = produto.pmc || produto['preco-fabrica'] || 0; const preco = produto.pmc || produto['preco-fabrica'] || 0;
const quantidade = quantidades[produto.$id] || 1; const quantidade = quantidades[produto.$id] || 1;
return ( return (
<div key={produto.$id} className="p-4 hover:bg-gray-50 transition-colors"> <div key={produto.$id} className="p-4 hover:bg-gray-50 transition-colors">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex-1"> <div className="flex-1">
<h3 className="font-medium text-gray-900 mb-1"> <h3 className="font-medium text-gray-900 mb-1">{produto.descricao}</h3>
{produto.descricao}
</h3>
<div className="flex items-center gap-4 text-sm text-gray-500"> <div className="flex items-center gap-4 text-sm text-gray-500">
<span>³digo: {produto['codigo-interno']}</span> <span>Código: {produto['codigo-interno']}</span>
{produto['codigo-ean'] && ( {produto['codigo-ean'] && <span>EAN: {produto['codigo-ean']}</span>}
<span>EAN: {produto['codigo-ean']}</span>
)}
{produto.laboratorios?.length > 0 ? ( {produto.laboratorios?.length > 0 ? (
<span>Lab: {produto.laboratorios[0].nome || produto.laboratorios[0]}</span> <span>Lab: {produto.laboratorios[0].nome || produto.laboratorios[0]}</span>
) : produto.laboratorio && ( ) : produto.laboratorio && (
@ -131,29 +120,21 @@ const CatalogoComCarrinho: React.FC<CatalogoComCarrinhoProps> = ({ onAdicionarAo
)} )}
</div> </div>
<div className="mt-2"> <div className="mt-2">
<span className="text-lg font-bold text-green-600"> <span className="text-lg font-bold text-green-600">{formatarPreco(preco)}</span>
{formatarPreco(preco)}
</span>
{produto['preco-fabrica'] && produto.pmc && produto['preco-fabrica'] !== produto.pmc && ( {produto['preco-fabrica'] && produto.pmc && produto['preco-fabrica'] !== produto.pmc && (
<span className="ml-2 text-sm text-gray-500 line-through"> <span className="ml-2 text-sm text-gray-500 line-through">{formatarPreco(produto['preco-fabrica'])}</span>
{formatarPreco(produto['preco-fabrica'])}
</span>
)} )}
{produto['desconto-comercial'] && produto['desconto-comercial'] > 0 && ( {produto['desconto-comercial'] && produto['desconto-comercial'] > 0 && (
<div className="text-xs text-orange-600 mt-1"> <div className="text-xs text-orange-600 mt-1">Desconto: {formatarPreco(produto['desconto-comercial'])}</div>
Desconto: {formatarPreco(produto['desconto-comercial'])}
</div>
)} )}
</div> </div>
</div> </div>
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
{/* Quantidade */}
<div className="flex items-center border border-gray-300 rounded-lg px-3 py-1"> <div className="flex items-center border border-gray-300 rounded-lg px-3 py-1">
<span className="text-sm font-medium">Qtd: {quantidade}</span> <span className="text-sm font-medium">Qtd: {quantidade}</span>
</div> </div>
{/* Botão Adicionar ao Carrinho */}
<button <button
onClick={() => adicionarAoCarrinho(produto)} onClick={() => adicionarAoCarrinho(produto)}
className="flex items-center gap-2 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors" className="flex items-center gap-2 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
@ -170,7 +151,6 @@ const CatalogoComCarrinho: React.FC<CatalogoComCarrinhoProps> = ({ onAdicionarAo
</div> </div>
</div> </div>
{/* Carrinho */}
<div className="lg:col-span-1"> <div className="lg:col-span-1">
<div className="bg-white rounded-lg shadow sticky top-4"> <div className="bg-white rounded-lg shadow sticky top-4">
<div className="p-4 border-b border-gray-200"> <div className="p-4 border-b border-gray-200">
@ -179,7 +159,7 @@ const CatalogoComCarrinho: React.FC<CatalogoComCarrinhoProps> = ({ onAdicionarAo
Carrinho ({carrinho.length}) Carrinho ({carrinho.length})
</h3> </h3>
</div> </div>
<div className="p-4"> <div className="p-4">
{carrinho.length === 0 ? ( {carrinho.length === 0 ? (
<p className="text-gray-500 text-center py-8">Carrinho vazio</p> <p className="text-gray-500 text-center py-8">Carrinho vazio</p>
@ -190,17 +170,11 @@ const CatalogoComCarrinho: React.FC<CatalogoComCarrinhoProps> = ({ onAdicionarAo
return ( return (
<div key={item.produto.$id} className="flex items-center justify-between p-3 bg-gray-50 rounded-lg"> <div key={item.produto.$id} className="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
<div className="flex-1"> <div className="flex-1">
<h4 className="font-medium text-sm text-gray-900 line-clamp-2"> <h4 className="font-medium text-sm text-gray-900 line-clamp-2">{item.produto.descricao}</h4>
{item.produto.descricao} <p className="text-xs text-gray-500">{formatarPreco(preco)} x {item.quantidade}</p>
</h4>
<p className="text-xs text-gray-500">
{formatarPreco(preco)} x {item.quantidade}
</p>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="font-medium text-green-600"> <span className="font-medium text-green-600">{formatarPreco(preco * item.quantidade)}</span>
{formatarPreco(preco * item.quantidade)}
</span>
<button <button
onClick={() => removerDoCarrinho(item.produto.$id)} onClick={() => removerDoCarrinho(item.produto.$id)}
className="text-red-500 hover:text-red-700 transition-colors" className="text-red-500 hover:text-red-700 transition-colors"
@ -211,15 +185,13 @@ const CatalogoComCarrinho: React.FC<CatalogoComCarrinhoProps> = ({ onAdicionarAo
</div> </div>
); );
})} })}
<div className="border-t border-gray-200 pt-3 mt-4"> <div className="border-t border-gray-200 pt-3 mt-4">
<div className="flex justify-between items-center mb-4"> <div className="flex justify-between items-center mb-4">
<span className="font-semibold text-gray-900">Total:</span> <span className="font-semibold text-gray-900">Total:</span>
<span className="font-bold text-lg text-green-600"> <span className="font-bold text-lg text-green-600">{formatarPreco(calcularTotal())}</span>
{formatarPreco(calcularTotal())}
</span>
</div> </div>
<button className="w-full bg-green-600 text-white py-3 rounded-lg hover:bg-green-700 transition-colors font-medium"> <button className="w-full bg-green-600 text-white py-3 rounded-lg hover:bg-green-700 transition-colors font-medium">
Finalizar Compra Finalizar Compra
</button> </button>
@ -235,4 +207,3 @@ const CatalogoComCarrinho: React.FC<CatalogoComCarrinhoProps> = ({ onAdicionarAo
}; };
export default CatalogoComCarrinho; export default CatalogoComCarrinho;

View file

@ -1,12 +1,12 @@
// @ts-nocheck // @ts-nocheck
'use client'; 'use client';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { Models } from '@/lib/appwrite';
import { useCarrinho } from '@/contexts/CarrinhoContext'; import { useCarrinho } from '@/contexts/CarrinhoContext';
import { ShoppingCartIcon, PlusIcon, CheckIcon } from '@heroicons/react/24/outline'; import { ShoppingCartIcon, CheckIcon } from '@heroicons/react/24/outline';
import { CatalogoProdutoDocument } from '@/types/legacyEntities';
interface CatalogoProdutosComprasProps { interface CatalogoProdutosComprasProps {
produtos: Models.Document[]; produtos: CatalogoProdutoDocument[];
loading: boolean; loading: boolean;
isChangingPage?: boolean; isChangingPage?: boolean;
totalProdutos: number; totalProdutos: number;
@ -14,7 +14,7 @@ interface CatalogoProdutosComprasProps {
pageSize: number; pageSize: number;
onPrevPage: () => void; onPrevPage: () => void;
onNextPage: () => void; onNextPage: () => void;
onRowClick?: (produto: Models.Document) => void; onRowClick?: (produto: CatalogoProdutoDocument) => void;
} }
const CatalogoProdutosCompras: React.FC<CatalogoProdutosComprasProps> = ({ const CatalogoProdutosCompras: React.FC<CatalogoProdutosComprasProps> = ({
@ -31,27 +31,23 @@ const CatalogoProdutosCompras: React.FC<CatalogoProdutosComprasProps> = ({
const { adicionarItem } = useCarrinho(); const { adicionarItem } = useCarrinho();
const [produtosAdicionados, setProdutosAdicionados] = useState<Set<string>>(new Set()); const [produtosAdicionados, setProdutosAdicionados] = useState<Set<string>>(new Set());
const formatarPreco = (preco: number) => { const formatarPreco = (preco: number) => new Intl.NumberFormat('pt-BR', {
return new Intl.NumberFormat('pt-BR', { style: 'currency',
style: 'currency', currency: 'BRL'
currency: 'BRL' }).format(preco);
}).format(preco);
};
const obterPreco = (produto: Models.Document) => { const obterPreco = (produto: CatalogoProdutoDocument) => {
return produto['preco-unitario'] || produto['preco-fabrica'] || produto.pmc || 0; return produto['preco-unitario'] || produto['preco-fabrica'] || produto.pmc || 0;
}; };
const obterQuantidadeDisponivel = (produto: Models.Document) => { const obterQuantidadeDisponivel = (produto: CatalogoProdutoDocument) => {
// Usar quantidade real do produto ou retornar 0 se não informado return produto.quantidade || produto.estoque || 0;
return (produto as any).quantidade || (produto as any).estoque || 0;
}; };
const handleAdicionarAoCarrinho = (produto: Models.Document, event: React.MouseEvent) => { const handleAdicionarAoCarrinho = (produto: CatalogoProdutoDocument, event: React.MouseEvent) => {
event.stopPropagation(); // Evita que o clique no botão dispare o onRowClick event.stopPropagation();
adicionarItem(produto, 1); adicionarItem(produto as any, 1);
// Adicionar feedback visual temporário
setProdutosAdicionados(prev => new Set(prev).add(produto.$id)); setProdutosAdicionados(prev => new Set(prev).add(produto.$id));
setTimeout(() => { setTimeout(() => {
setProdutosAdicionados(prev => { setProdutosAdicionados(prev => {
@ -79,7 +75,6 @@ const CatalogoProdutosCompras: React.FC<CatalogoProdutosComprasProps> = ({
return ( return (
<div className="bg-white rounded-lg shadow-sm"> <div className="bg-white rounded-lg shadow-sm">
{/* Grid de Produtos */}
<div className="p-6"> <div className="p-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
{produtos.map(produto => { {produtos.map(produto => {
@ -87,20 +82,19 @@ const CatalogoProdutosCompras: React.FC<CatalogoProdutosComprasProps> = ({
const quantidadeDisponivel = obterQuantidadeDisponivel(produto); const quantidadeDisponivel = obterQuantidadeDisponivel(produto);
const foiAdicionado = produtosAdicionados.has(produto.$id); const foiAdicionado = produtosAdicionados.has(produto.$id);
const estoqueStatus = quantidadeDisponivel > 20 ? 'alto' : quantidadeDisponivel > 5 ? 'medio' : 'baixo'; const estoqueStatus = quantidadeDisponivel > 20 ? 'alto' : quantidadeDisponivel > 5 ? 'medio' : 'baixo';
return ( return (
<div <div
key={produto.$id} key={produto.$id}
className={`bg-white border rounded-lg p-4 hover:shadow-md transition-all duration-200 cursor-pointer relative ${ className={`bg-white border rounded-lg p-4 hover:shadow-md transition-all duration-200 cursor-pointer relative ${
estoqueStatus === 'baixo' estoqueStatus === 'baixo'
? 'border-red-200 bg-red-50/30' ? 'border-red-200 bg-red-50/30'
: estoqueStatus === 'medio' : estoqueStatus === 'medio'
? 'border-yellow-200 bg-yellow-50/30' ? 'border-yellow-200 bg-yellow-50/30'
: 'border-gray-200' : 'border-gray-200'
}`} }`}
onClick={() => onRowClick && onRowClick(produto)} onClick={() => onRowClick && onRowClick(produto)}
> >
{/* Indicador de Estoque Baixo */}
{estoqueStatus === 'baixo' && ( {estoqueStatus === 'baixo' && (
<div className="absolute top-2 right-2"> <div className="absolute top-2 right-2">
<span className="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-red-100 text-red-800"> <span className="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-red-100 text-red-800">
@ -111,36 +105,25 @@ const CatalogoProdutosCompras: React.FC<CatalogoProdutosComprasProps> = ({
{estoqueStatus === 'medio' && ( {estoqueStatus === 'medio' && (
<div className="absolute top-2 right-2"> <div className="absolute top-2 right-2">
<span className="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800"> <span className="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800">
Estoque Médio Estoque Médio
</span> </span>
</div> </div>
)} )}
{/* Header do Card */}
<div className="flex items-start justify-between mb-3"> <div className="flex items-start justify-between mb-3">
<div className={`flex-1 ${estoqueStatus !== 'alto' ? 'pr-20' : ''}`}> <div className={`flex-1 ${estoqueStatus !== 'alto' ? 'pr-20' : ''}`}>
<h3 className="text-sm font-semibold text-gray-900 line-clamp-2 mb-1"> <h3 className="text-sm font-semibold text-gray-900 line-clamp-2 mb-1">{produto.descricao}</h3>
{produto.descricao} <p className="text-xs text-gray-500">Código: {produto['codigo-interno']}</p>
</h3>
<p className="text-xs text-gray-500">
³digo: {produto['codigo-interno']}
</p>
</div> </div>
</div> </div>
{/* Informações do Produto */}
<div className="space-y-3 mb-4"> <div className="space-y-3 mb-4">
{produto['codigo-ean'] && ( {produto['codigo-ean'] && <p className="text-xs text-gray-500">EAN: {produto['codigo-ean']}</p>}
<p className="text-xs text-gray-500">
EAN: {produto['codigo-ean']}
</p>
)}
{/* Laboratório */}
{produto.laboratorios?.length > 0 ? ( {produto.laboratorios?.length > 0 ? (
<div className="flex flex-wrap gap-1"> <div className="flex flex-wrap gap-1">
{produto.laboratorios.slice(0, 2).map((lab: any) => ( {produto.laboratorios.slice(0, 2).map((lab: any, index: number) => (
<span key={lab.$id} className="inline-block bg-gray-100 text-gray-700 px-2 py-1 rounded text-xs"> <span key={lab.$id || index} className="inline-block bg-gray-100 text-gray-700 px-2 py-1 rounded text-xs">
{lab.nome} {lab.nome || lab}
</span> </span>
))} ))}
{produto.laboratorios.length > 2 && ( {produto.laboratorios.length > 2 && (
@ -148,24 +131,22 @@ const CatalogoProdutosCompras: React.FC<CatalogoProdutosComprasProps> = ({
)} )}
</div> </div>
) : ( ) : (
<span className="text-xs text-gray-400">Sem laboratório</span> <span className="text-xs text-gray-400">Sem laboratório</span>
)} )}
{/* Categoria */}
{produto.categorias && ( {produto.categorias && (
<span className="inline-block bg-blue-100 text-blue-700 px-2 py-1 rounded text-xs"> <span className="inline-block bg-blue-100 text-blue-700 px-2 py-1 rounded text-xs">
{produto.categorias.nome} {produto.categorias.nome}
</span> </span>
)} )}
{/* Quantidade Disponível */}
<div className="flex items-center justify-between text-xs"> <div className="flex items-center justify-between text-xs">
<span className="text-gray-600">Disponível:</span> <span className="text-gray-600">Disponível:</span>
<span className={`font-medium ${ <span className={`font-medium ${
estoqueStatus === 'alto' estoqueStatus === 'alto'
? 'text-green-600' ? 'text-green-600'
: estoqueStatus === 'medio' : estoqueStatus === 'medio'
? 'text-yellow-600' ? 'text-yellow-600'
: 'text-red-600' : 'text-red-600'
}`}> }`}>
{quantidadeDisponivel} unidades {quantidadeDisponivel} unidades
@ -173,44 +154,32 @@ const CatalogoProdutosCompras: React.FC<CatalogoProdutosComprasProps> = ({
</div> </div>
</div> </div>
{/* Seção de Preços */}
<div className="space-y-2 mb-4"> <div className="space-y-2 mb-4">
{/* Preço Principal */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<span className="text-sm text-gray-600">Preço:</span> <span className="text-sm text-gray-600">Preço:</span>
{preco > 0 ? ( {preco > 0 ? (
<span className="text-lg font-bold text-green-600"> <span className="text-lg font-bold text-green-600">{formatarPreco(preco)}</span>
{formatarPreco(preco)}
</span>
) : ( ) : (
<span className="text-sm text-gray-400"> <span className="text-sm text-gray-400">Não informado</span>
£o informado
</span>
)} )}
</div> </div>
{/* Preços Adicionais */}
<div className="grid grid-cols-2 gap-2 text-xs"> <div className="grid grid-cols-2 gap-2 text-xs">
{produto['preco-fabrica'] && produto['preco-fabrica'] !== preco && ( {produto['preco-fabrica'] && produto['preco-fabrica'] !== preco && (
<div className="text-center"> <div className="text-center">
<span className="block text-gray-500">¡brica</span> <span className="block text-gray-500">Fábrica</span>
<span className="font-medium text-gray-700"> <span className="font-medium text-gray-700">{formatarPreco(produto['preco-fabrica'])}</span>
{formatarPreco(produto['preco-fabrica'])}
</span>
</div> </div>
)} )}
{produto.pmc && produto.pmc !== preco && ( {produto.pmc && produto.pmc !== preco && (
<div className="text-center"> <div className="text-center">
<span className="block text-gray-500">PMC</span> <span className="block text-gray-500">PMC</span>
<span className="font-medium text-gray-700"> <span className="font-medium text-gray-700">{formatarPreco(produto.pmc)}</span>
{formatarPreco(produto.pmc)}
</span>
</div> </div>
)} )}
</div> </div>
</div> </div>
{/* Botão de Ação */}
<div className="flex justify-center"> <div className="flex justify-center">
<button <button
onClick={(e) => handleAdicionarAoCarrinho(produto, e)} onClick={(e) => handleAdicionarAoCarrinho(produto, e)}
@ -241,17 +210,15 @@ const CatalogoProdutosCompras: React.FC<CatalogoProdutosComprasProps> = ({
})} })}
</div> </div>
{/* Mensagem quando não há produtos */}
{produtos.length === 0 && ( {produtos.length === 0 && (
<div className="text-center py-12"> <div className="text-center py-12">
<ShoppingCartIcon className="w-12 h-12 text-gray-300 mx-auto mb-4" /> <ShoppingCartIcon className="w-12 h-12 text-gray-300 mx-auto mb-4" />
<h3 className="text-lg font-medium text-gray-900 mb-2">Nenhum produto encontrado</h3> <h3 className="text-lg font-medium text-gray-900 mb-2">Nenhum produto encontrado</h3>
<p className="text-gray-500">Tente ajustar os filtros de busca ou verifique se há produtos cadastrados.</p> <p className="text-gray-500">Tente ajustar os filtros de busca ou verifique se há produtos cadastrados.</p>
</div> </div>
)} )}
</div> </div>
{/* Paginação */}
{produtos.length > 0 && ( {produtos.length > 0 && (
<div className="bg-gray-50 border-t border-gray-200 px-6 py-4"> <div className="bg-gray-50 border-t border-gray-200 px-6 py-4">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
@ -263,15 +230,13 @@ const CatalogoProdutosCompras: React.FC<CatalogoProdutosComprasProps> = ({
> >
Anterior Anterior
</button> </button>
<span className="px-3 py-2 text-sm text-gray-700 font-medium"> <span className="px-3 py-2 text-sm text-gray-700 font-medium">Página {currentPage} de {totalPages}</span>
¡gina {currentPage} de {totalPages}
</span>
<button <button
onClick={onNextPage} onClick={onNextPage}
disabled={currentPage >= totalPages || isChangingPage} disabled={currentPage >= totalPages || isChangingPage}
className="px-3 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-200" className="px-3 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-200"
> >
Próxima Próxima
</button> </button>
</div> </div>
<div className="text-sm text-gray-700 font-medium"> <div className="text-sm text-gray-700 font-medium">

View file

@ -1,18 +1,8 @@
import { Models } from "@/lib/appwrite"; import { CatalogoProdutoDocument } from '@/types/legacyEntities';
export interface CatalogoProdutoData extends Models.Document { export interface CatalogoProdutoData extends CatalogoProdutoDocument {
descricao: string; 'preco-original'?: number;
nome?: string; 'preco-atual'?: number;
"codigo-interno"?: string;
"codigo-ean"?: string;
"preco-original"?: number;
"preco-atual"?: number;
"preco-fabrica"?: number;
pmc?: number;
quantidade?: number;
laboratorio?: string;
categoria?: string;
subcategoria?: string;
} }
export interface ApiResponse<T = unknown> { export interface ApiResponse<T = unknown> {
@ -28,14 +18,14 @@ export interface PaginatedResponse<T> extends ApiResponse<T[]> {
} }
class CatalogoProdutoService { class CatalogoProdutoService {
private readonly baseUrl = "/api/catalogo-produtos"; private readonly baseUrl = '/api/catalogo-produtos';
private async parseResponse<T>(response: Response): Promise<T> { private async parseResponse<T>(response: Response): Promise<T> {
const data = await response.json().catch(() => ({})); const data = await response.json().catch(() => ({}));
if (!response.ok) { if (!response.ok) {
const message = const message =
typeof data?.error === "string" typeof data?.error === 'string'
? data.error ? data.error
: `Erro HTTP ${response.status}`; : `Erro HTTP ${response.status}`;
throw new Error(message); throw new Error(message);
@ -44,36 +34,36 @@ class CatalogoProdutoService {
return data as T; return data as T;
} }
async listar(page = 1, limit = 10, termo = ""): Promise<PaginatedResponse<CatalogoProdutoData>> { async listar(page = 1, limit = 10, termo = ''): Promise<PaginatedResponse<CatalogoProdutoData>> {
const params = new URLSearchParams({ const params = new URLSearchParams({
page: String(page), page: String(page),
limit: String(limit), limit: String(limit),
}); });
if (termo.trim()) { if (termo.trim()) {
params.set("q", termo.trim()); params.set('q', termo.trim());
} }
const response = await fetch(`${this.baseUrl}?${params.toString()}`, { const response = await fetch(`${this.baseUrl}?${params.toString()}`, {
method: "GET", method: 'GET',
cache: "no-store", cache: 'no-store',
}); });
const data = await this.parseResponse<Partial<PaginatedResponse<CatalogoProdutoData>>>(response); const data = await this.parseResponse<Partial<PaginatedResponse<CatalogoProdutoData>>>(response);
const documents = Array.isArray(data.documents) ? data.documents : []; const documents = Array.isArray(data.documents) ? data.documents : [];
const filteredDocuments = termo.trim() const filteredDocuments = termo.trim()
? documents.filter((produto) => { ? documents.filter(produto => {
const values = [ const values = [
produto.descricao, produto.descricao,
produto.nome, produto.nome,
produto["codigo-interno"], produto['codigo-interno'],
produto["codigo-ean"], produto['codigo-ean'],
] ]
.filter(Boolean) .filter(Boolean)
.map((value) => String(value).toLowerCase()); .map(value => String(value).toLowerCase());
return values.some((value) => value.includes(termo.trim().toLowerCase())); return values.some(value => value.includes(termo.trim().toLowerCase()));
}) })
: documents; : documents;
@ -90,8 +80,8 @@ class CatalogoProdutoService {
async obterPorId(id: string): Promise<ApiResponse<CatalogoProdutoData>> { async obterPorId(id: string): Promise<ApiResponse<CatalogoProdutoData>> {
const response = await fetch(`${this.baseUrl}/${id}`, { const response = await fetch(`${this.baseUrl}/${id}`, {
method: "GET", method: 'GET',
cache: "no-store", cache: 'no-store',
}); });
const data = await this.parseResponse<ApiResponse<CatalogoProdutoData>>(response); const data = await this.parseResponse<ApiResponse<CatalogoProdutoData>>(response);
@ -103,9 +93,9 @@ class CatalogoProdutoService {
async criar(produtoData: Partial<CatalogoProdutoData>): Promise<ApiResponse<CatalogoProdutoData>> { async criar(produtoData: Partial<CatalogoProdutoData>): Promise<ApiResponse<CatalogoProdutoData>> {
const response = await fetch(this.baseUrl, { const response = await fetch(this.baseUrl, {
method: "POST", method: 'POST',
headers: { headers: {
"Content-Type": "application/json", 'Content-Type': 'application/json',
}, },
body: JSON.stringify(produtoData), body: JSON.stringify(produtoData),
}); });
@ -119,9 +109,9 @@ class CatalogoProdutoService {
async atualizar(id: string, produtoData: Partial<CatalogoProdutoData>): Promise<ApiResponse<CatalogoProdutoData>> { async atualizar(id: string, produtoData: Partial<CatalogoProdutoData>): Promise<ApiResponse<CatalogoProdutoData>> {
const response = await fetch(`${this.baseUrl}/${id}`, { const response = await fetch(`${this.baseUrl}/${id}`, {
method: "PATCH", method: 'PATCH',
headers: { headers: {
"Content-Type": "application/json", 'Content-Type': 'application/json',
}, },
body: JSON.stringify(produtoData), body: JSON.stringify(produtoData),
}); });
@ -135,7 +125,7 @@ class CatalogoProdutoService {
async deletar(id: string): Promise<ApiResponse<null>> { async deletar(id: string): Promise<ApiResponse<null>> {
const response = await fetch(`${this.baseUrl}/${id}`, { const response = await fetch(`${this.baseUrl}/${id}`, {
method: "DELETE", method: 'DELETE',
}); });
await this.parseResponse<ApiResponse<null>>(response); await this.parseResponse<ApiResponse<null>>(response);
@ -145,4 +135,3 @@ class CatalogoProdutoService {
export const catalogoProdutoService = new CatalogoProdutoService(); export const catalogoProdutoService = new CatalogoProdutoService();
export type ProdutoData = CatalogoProdutoData; export type ProdutoData = CatalogoProdutoData;