315 lines
9.4 KiB
TypeScript
315 lines
9.4 KiB
TypeScript
"use client";
|
|
import React, {
|
|
createContext,
|
|
useContext,
|
|
useState,
|
|
useEffect,
|
|
ReactNode,
|
|
} from "react";
|
|
import { produtoService } from "@/services/produtoService";
|
|
import { ProdutoCompleto } from "@/services/produtosVendaService";
|
|
import { carrinhoApiService } from "@/services/carrinhoApiService";
|
|
import { toast } from "react-hot-toast";
|
|
|
|
interface ItemCarrinho {
|
|
produto: ProdutoCompleto;
|
|
quantidade: number;
|
|
}
|
|
|
|
interface CarrinhoContextType {
|
|
itens: ItemCarrinho[];
|
|
totalItens: number;
|
|
valorTotal: number;
|
|
carrinhoId: string | null;
|
|
sincronizandoApi: boolean;
|
|
adicionarItem: (
|
|
produto: ProdutoCompleto,
|
|
quantidade?: number
|
|
) => Promise<void>;
|
|
removerItem: (produtoId: string) => Promise<void>;
|
|
atualizarQuantidade: (produtoId: string, quantidade: number) => Promise<void>;
|
|
limparCarrinho: (restaurarEstoque?: boolean) => Promise<void>;
|
|
isCarrinhoAberto: boolean;
|
|
setIsCarrinhoAberto: (aberto: boolean) => void;
|
|
}
|
|
|
|
const CarrinhoContext = createContext<CarrinhoContextType | undefined>(
|
|
undefined
|
|
);
|
|
|
|
export { CarrinhoContext };
|
|
|
|
export const useCarrinho = () => {
|
|
const context = useContext(CarrinhoContext);
|
|
if (!context) {
|
|
throw new Error("useCarrinho deve ser usado dentro de um CarrinhoProvider");
|
|
}
|
|
return context;
|
|
};
|
|
|
|
interface CarrinhoProviderProps {
|
|
children: ReactNode;
|
|
}
|
|
|
|
// Função para obter o preço do produto de forma consistente
|
|
const obterPreco = (produto: ProdutoCompleto): number => {
|
|
const produtoData = produto as any;
|
|
// Retorna apenas o preco_final, se não existir retorna 0
|
|
return produtoData.preco_final || 0;
|
|
};
|
|
|
|
export const CarrinhoProvider: React.FC<CarrinhoProviderProps> = ({
|
|
children,
|
|
}) => {
|
|
const [itens, setItens] = useState<ItemCarrinho[]>([]);
|
|
const [isCarrinhoAberto, setIsCarrinhoAberto] = useState(false);
|
|
const [carrinhoId, setCarrinhoId] = useState<string | null>(null);
|
|
const [sincronizandoApi, setSincronizandoApi] = useState(false);
|
|
|
|
|
|
// Carregar carrinho do localStorage ao inicializar
|
|
useEffect(() => {
|
|
setTimeout(() => {
|
|
try {
|
|
const carrinhoSalvo = localStorage.getItem("carrinho-saveinmed");
|
|
const carrinhoIdSalvo = localStorage.getItem("carrinho-id");
|
|
|
|
if (carrinhoSalvo) {
|
|
const carrinhoParsed = JSON.parse(carrinhoSalvo);
|
|
setItens(carrinhoParsed);
|
|
}
|
|
|
|
if (carrinhoIdSalvo) {
|
|
setCarrinhoId(carrinhoIdSalvo);
|
|
}
|
|
} catch (error) {
|
|
}
|
|
}, 100);
|
|
}, []);
|
|
|
|
// Salvar carrinho no localStorage sempre que houver mudanças
|
|
useEffect(() => {
|
|
localStorage.setItem("carrinho-saveinmed", JSON.stringify(itens));
|
|
}, [itens]);
|
|
|
|
// Sincronizar com API BFF quando itens mudam (com debounce)
|
|
useEffect(() => {
|
|
if (itens.length === 0 && carrinhoId) {
|
|
// Verificar se estamos em páginas de checkout/pagamento
|
|
const isCheckoutPage = typeof window !== 'undefined' &&
|
|
(window.location.pathname.includes('/checkout') ||
|
|
window.location.pathname.includes('/pagamento'));
|
|
|
|
if (!isCheckoutPage) {
|
|
// Se não há itens mas há carrinho na API, excluir (apenas se não estamos no checkout)
|
|
setSincronizandoApi(true);
|
|
carrinhoApiService.excluir(carrinhoId)
|
|
.then(response => {
|
|
if (response.success) {
|
|
setCarrinhoId(null);
|
|
localStorage.removeItem("carrinho-id");
|
|
} else {
|
|
console.error("❌ [CARRINHO API] Erro ao excluir:", response.error);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error("💥 [CARRINHO API] Erro na exclusão:", error);
|
|
})
|
|
.finally(() => {
|
|
setSincronizandoApi(false);
|
|
});
|
|
} else {
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (itens.length === 0) return; // Não fazer nada se não há itens
|
|
|
|
// Debounce: aguardar 1 segundo sem mudanças antes de sincronizar
|
|
const timeoutId = setTimeout(() => {
|
|
setSincronizandoApi(true);
|
|
|
|
if (carrinhoId) {
|
|
// Atualizar carrinho existente
|
|
carrinhoApiService.atualizar(carrinhoId, itens)
|
|
.then(response => {
|
|
if (response.success) {
|
|
} else {
|
|
console.error("❌ [CARRINHO API] Erro ao atualizar:", response.error);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error("💥 [CARRINHO API] Erro na atualização:", error);
|
|
})
|
|
.finally(() => {
|
|
setSincronizandoApi(false);
|
|
});
|
|
} else {
|
|
// Criar novo carrinho
|
|
carrinhoApiService.criar(itens)
|
|
.then(response => {
|
|
if (response.success && response.data) {
|
|
const novoCarrinhoId = response.data.$id || response.data.id;
|
|
if (novoCarrinhoId) {
|
|
setCarrinhoId(novoCarrinhoId);
|
|
localStorage.setItem("carrinho-id", novoCarrinhoId);
|
|
}
|
|
} else {
|
|
console.error("❌ [CARRINHO API] Erro ao criar:", response.error);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error("💥 [CARRINHO API] Erro na criação:", error);
|
|
})
|
|
.finally(() => {
|
|
setSincronizandoApi(false);
|
|
});
|
|
}
|
|
}, 1000);
|
|
|
|
return () => clearTimeout(timeoutId);
|
|
}, [itens, carrinhoId]);
|
|
|
|
const adicionarItem = async (
|
|
produto: ProdutoCompleto,
|
|
quantidade: number = 1
|
|
) => {
|
|
// Obter estoque disponível do produto
|
|
const estoqueDisponivel = produto.quantidade_estoque || 0;
|
|
|
|
setItens((prevItens) => {
|
|
const itemExistente = prevItens.find(
|
|
(item) => item.produto.id === produto.id
|
|
);
|
|
|
|
if (itemExistente) {
|
|
const novaQuantidade = itemExistente.quantidade + quantidade;
|
|
|
|
// Verificar se a nova quantidade não excede o estoque
|
|
if (novaQuantidade > estoqueDisponivel) {
|
|
console.warn(
|
|
`Tentativa de adicionar mais itens do que disponível em estoque. Disponível: ${estoqueDisponivel}, Tentando adicionar: ${novaQuantidade}`
|
|
);
|
|
toast.error(
|
|
`Estoque insuficiente! Disponível: ${estoqueDisponivel} unidades`
|
|
);
|
|
return prevItens; // Não adiciona se exceder o estoque
|
|
}
|
|
|
|
toast.success("Produto adicionado ao carrinho!");
|
|
|
|
return prevItens.map((item) =>
|
|
item.produto.id === produto.id
|
|
? { ...item, quantidade: novaQuantidade }
|
|
: item
|
|
);
|
|
} else {
|
|
// Verificar se a quantidade inicial não excede o estoque
|
|
if (quantidade > estoqueDisponivel) {
|
|
console.warn(
|
|
`Tentativa de adicionar mais itens do que disponível em estoque. Disponível: ${estoqueDisponivel}, Tentando adicionar: ${quantidade}`
|
|
);
|
|
toast.error(
|
|
`Estoque insuficiente! Disponível: ${estoqueDisponivel} unidades`
|
|
);
|
|
return prevItens; // Não adiciona se exceder o estoque
|
|
}
|
|
|
|
toast.success("Produto adicionado ao carrinho!");
|
|
|
|
return [...prevItens, { produto, quantidade }];
|
|
}
|
|
});
|
|
};
|
|
|
|
const removerItem = async (produtoId: string) => {
|
|
toast.success("Item removido do carrinho!");
|
|
setItens((prevItens) =>
|
|
prevItens.filter((item) => item.produto.id !== produtoId)
|
|
);
|
|
};
|
|
|
|
const atualizarQuantidade = async (produtoId: string, quantidade: number) => {
|
|
if (quantidade <= 0) {
|
|
await removerItem(produtoId);
|
|
return;
|
|
}
|
|
|
|
// Encontrar o item atual
|
|
const itemAtual = itens.find((item) => item.produto.id === produtoId);
|
|
|
|
if (itemAtual) {
|
|
// Verificar se há estoque suficiente para a nova quantidade
|
|
const estoqueDisponivel = itemAtual.produto.quantidade_estoque || 0;
|
|
if (quantidade > estoqueDisponivel) {
|
|
toast.error(
|
|
`Estoque insuficiente! Disponível: ${estoqueDisponivel} unidades`
|
|
);
|
|
return;
|
|
}
|
|
|
|
setItens((prevItens) =>
|
|
prevItens.map((item) => {
|
|
if (item.produto.id === produtoId) {
|
|
return { ...item, quantidade };
|
|
}
|
|
return item;
|
|
})
|
|
);
|
|
|
|
toast.success("Quantidade atualizada!");
|
|
}
|
|
};
|
|
|
|
const limparCarrinho = async (restaurarEstoque: boolean = false) => {
|
|
// Primeiro, excluir carrinho da API se existir
|
|
if (carrinhoId) {
|
|
try {
|
|
setSincronizandoApi(true);
|
|
const response = await carrinhoApiService.excluir(carrinhoId);
|
|
if (response.success) {
|
|
} else {
|
|
console.error("❌ [CARRINHO API] Erro ao excluir:", response.error);
|
|
}
|
|
} catch (error) {
|
|
console.error("💥 [CARRINHO API] Erro na exclusão:", error);
|
|
} finally {
|
|
setSincronizandoApi(false);
|
|
}
|
|
|
|
setCarrinhoId(null);
|
|
localStorage.removeItem("carrinho-id");
|
|
}
|
|
|
|
setItens([]);
|
|
toast.success("Carrinho limpo!");
|
|
};
|
|
|
|
const totalItens = itens.reduce((total, item) => total + item.quantidade, 0);
|
|
|
|
// Atualizar o cálculo do valor total para usar a nova função de preço
|
|
const valorTotal = itens.reduce((total, item) => {
|
|
const preco = obterPreco(item.produto);
|
|
return total + preco * item.quantidade;
|
|
}, 0);
|
|
|
|
const value: CarrinhoContextType = {
|
|
itens,
|
|
totalItens,
|
|
valorTotal,
|
|
carrinhoId,
|
|
sincronizandoApi,
|
|
adicionarItem,
|
|
removerItem,
|
|
atualizarQuantidade,
|
|
limparCarrinho,
|
|
isCarrinhoAberto,
|
|
setIsCarrinhoAberto,
|
|
};
|
|
|
|
return (
|
|
<CarrinhoContext.Provider value={value}>
|
|
{children}
|
|
</CarrinhoContext.Provider>
|
|
);
|
|
};
|