diff --git a/saveinmed-frontend/src/app/compras-internas/page.tsx b/saveinmed-frontend/src/app/compras-internas/page.tsx new file mode 100644 index 0000000..778397c --- /dev/null +++ b/saveinmed-frontend/src/app/compras-internas/page.tsx @@ -0,0 +1,261 @@ +"use client"; + +import React, { useEffect, useMemo, useState } from "react"; +import Header from "@/components/Header"; +import { empresaApiService } from "@/services/empresaApiService"; +import { produtosVendaService, ProdutoCompleto } from "@/services/produtosVendaService"; +import { MapPinIcon } from "@heroicons/react/24/outline"; + +interface Coordenadas { + lat: number; + lng: number; +} + +interface GrupoProdutos { + chave: string; + nome: string; + descricao?: string; + categoria?: string; + laboratorio?: string; + itens: ProdutoCompleto[]; +} + +const calcularDistanciaKm = (origem: Coordenadas, destino: Coordenadas): number => { + const raioTerraKm = 6371; + const dLat = ((destino.lat - origem.lat) * Math.PI) / 180; + const dLng = ((destino.lng - origem.lng) * Math.PI) / 180; + const lat1 = (origem.lat * Math.PI) / 180; + const lat2 = (destino.lat * Math.PI) / 180; + + const a = + Math.sin(dLat / 2) * Math.sin(dLat / 2) + + Math.sin(dLng / 2) * Math.sin(dLng / 2) * Math.cos(lat1) * Math.cos(lat2); + const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + return raioTerraKm * c; +}; + +const normalizarCoordenadas = (valor?: string | number | null): number | null => { + if (valor === undefined || valor === null) { + return null; + } + const numero = Number(valor); + if (!Number.isFinite(numero)) { + return null; + } + if (numero === 0) { + return null; + } + return numero; +}; + +const ComprasInternasPage = () => { + const [produtos, setProdutos] = useState([]); + const [loading, setLoading] = useState(true); + const [erro, setErro] = useState(null); + const [clienteCoords, setClienteCoords] = useState(null); + const [erroGeolocalizacao, setErroGeolocalizacao] = useState(null); + const [coordenadasEmpresas, setCoordenadasEmpresas] = useState>({}); + + useEffect(() => { + const carregarProdutos = async () => { + setLoading(true); + setErro(null); + + try { + const produtosCarregados = await produtosVendaService.buscarTodosProdutosCompletos(); + setProdutos(produtosCarregados); + + const empresaIds = Array.from( + new Set(produtosCarregados.map((produto) => produto.empresa_id).filter(Boolean)) + ) as string[]; + + if (empresaIds.length) { + const empresas = await Promise.all( + empresaIds.map(async (empresaId) => ({ + empresaId, + dados: await empresaApiService.buscarPorId(empresaId), + })) + ); + + const mapaCoordenadas: Record = {}; + empresas.forEach(({ empresaId, dados }) => { + const lat = normalizarCoordenadas(dados?.latitude ?? null); + const lng = normalizarCoordenadas(dados?.longitude ?? null); + if (lat !== null && lng !== null) { + mapaCoordenadas[empresaId] = { lat, lng }; + } + }); + + setCoordenadasEmpresas(mapaCoordenadas); + } + } catch (error) { + console.error("Erro ao carregar produtos para compra:", error); + setErro("Não foi possível carregar os produtos no momento."); + } finally { + setLoading(false); + } + }; + + carregarProdutos(); + }, []); + + useEffect(() => { + if (!navigator.geolocation) { + setErroGeolocalizacao("Geolocalização não suportada neste navegador."); + return; + } + + navigator.geolocation.getCurrentPosition( + (position) => { + setClienteCoords({ + lat: position.coords.latitude, + lng: position.coords.longitude, + }); + }, + (error) => { + console.warn("Erro ao obter geolocalização:", error); + setErroGeolocalizacao("Não foi possível obter sua localização."); + }, + { + enableHighAccuracy: true, + timeout: 10000, + maximumAge: 60000, + } + ); + }, []); + + const grupos = useMemo(() => { + const mapa = new Map(); + + produtos.forEach((produto) => { + const chave = produto.catalogo_id || produto.codigo_ean || produto.nome; + const grupoExistente = mapa.get(chave); + + if (grupoExistente) { + grupoExistente.itens.push(produto); + } else { + mapa.set(chave, { + chave, + nome: produto.nome || "Produto sem nome", + descricao: produto.descricao, + categoria: produto.cat_nome || produto.categoria, + laboratorio: produto.lab_nome || produto.laboratorio, + itens: [produto], + }); + } + }); + + return Array.from(mapa.values()).map((grupo) => ({ + ...grupo, + itens: [...grupo.itens].sort( + (a, b) => (a.preco_final || a.preco_venda) - (b.preco_final || b.preco_venda) + ), + })); + }, [produtos]); + + const obterDistancia = (produto: ProdutoCompleto): number | null => { + if (!clienteCoords || !produto.empresa_id) { + return null; + } + + const coordenadasEmpresa = coordenadasEmpresas[produto.empresa_id]; + if (!coordenadasEmpresa) { + return null; + } + + return calcularDistanciaKm(clienteCoords, coordenadasEmpresa); + }; + + return ( +
+
+
+
+

Produtos para compra interna

+

+ Lista interna de produtos para compra, agrupados por similaridade e com a distância estimada + até você. +

+ {erroGeolocalizacao && ( +
+ {erroGeolocalizacao} +
+ )} +
+ + {loading &&
Carregando produtos...
} + + {!loading && erro && ( +
+ {erro} +
+ )} + + {!loading && !erro && grupos.length === 0 && ( +
Nenhum produto disponível para compra no momento.
+ )} + +
+ {grupos.map((grupo) => ( +
+
+
+

{grupo.nome}

+ ({grupo.itens.length} oferta(s)) +
+ {grupo.descricao &&

{grupo.descricao}

} +
+ {grupo.categoria && Categoria: {grupo.categoria}} + {grupo.laboratorio && Laboratório: {grupo.laboratorio}} +
+
+ +
+ {grupo.itens.map((produto) => { + const distanciaKm = obterDistancia(produto); + const preco = produto.preco_final || produto.preco_venda; + + return ( +
+
+
Estoque
+
+ {produto.quantidade_estoque ?? 0} un. +
+
+
+
Preço
+
+ R$ {preco.toFixed(2).replace(".", ",")} +
+
+
+
+ + Distância +
+
+ {distanciaKm !== null ? `${distanciaKm.toFixed(1)} km` : "Indisponível"} +
+
+ {produto.data_validade && ( +
+ Validade: {new Date(produto.data_validade).toLocaleDateString("pt-BR")} +
+ )} +
+ ); + })} +
+
+ ))} +
+
+
+ ); +}; + +export default ComprasInternasPage;