import { useState, useEffect, useCallback } from "react"; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { useToast } from "@/hooks/use-toast"; import { RefreshCw, CheckCircle, XCircle, Loader2, Users, Package, ShoppingCart, FileText, ExternalLink, Building2, Link2 } from "lucide-react"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; interface Empresa { id: number; tenantId: number; razaoSocial: string; nomeFantasia: string | null; cnpj: string; tipo: string; plusEmpresaId: number | null; isActive: boolean; } interface SyncResult { success: boolean; created?: number; updated?: number; failed?: number; errors?: string[]; message?: string; } interface PlusStatus { connected: boolean; url?: string; message?: string; } export default function PlusSyncPanel({ tenantId }: { tenantId?: number }) { const { toast } = useToast(); const [empresas, setEmpresas] = useState([]); const [selectedEmpresa, setSelectedEmpresa] = useState(""); const [plusStatus, setPlusStatus] = useState(null); const [loading, setLoading] = useState(false); const [syncingCustomers, setSyncingCustomers] = useState(false); const [syncingProducts, setSyncingProducts] = useState(false); const [syncingSales, setSyncingSales] = useState(false); const [importingCustomers, setImportingCustomers] = useState(false); const [importingProducts, setImportingProducts] = useState(false); const [bindPlusId, setBindPlusId] = useState(""); const [lastResults, setLastResults] = useState>({}); const currentTenant = tenantId || 1; const loadEmpresas = useCallback(async () => { try { const res = await fetch(`/api/retail/plus/empresas?tenantId=${currentTenant}`); if (res.ok) { const data = await res.json(); setEmpresas(data); if (data.length > 0 && !selectedEmpresa) { setSelectedEmpresa(data[0].id.toString()); } } } catch (error) { console.error("Error loading empresas:", error); } }, [currentTenant, selectedEmpresa]); const checkStatus = useCallback(async () => { setLoading(true); try { const empresaId = selectedEmpresa ? `?empresaId=${selectedEmpresa}` : ""; const res = await fetch(`/api/retail/plus/status${empresaId}`); if (res.ok) { const data = await res.json(); setPlusStatus(data); } } catch (error) { setPlusStatus({ connected: false, message: "Erro ao verificar conexão" }); } finally { setLoading(false); } }, [selectedEmpresa]); useEffect(() => { loadEmpresas(); }, [loadEmpresas]); useEffect(() => { if (selectedEmpresa) checkStatus(); }, [selectedEmpresa, checkStatus]); const handleSync = async (action: string, setLoading: (v: boolean) => void) => { setLoading(true); try { const body: any = { tenantId: currentTenant }; if (selectedEmpresa) body.empresaId = parseInt(selectedEmpresa); const res = await fetch(`/api/retail/plus/${action}`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); const data = await res.json(); setLastResults(prev => ({ ...prev, [action]: data })); if (data.success) { toast({ title: "Sincronização concluída", description: `Criados: ${data.created || 0}, Atualizados: ${data.updated || 0}` }); } else { toast({ title: "Erro na sincronização", description: data.message || "Falha na operação", variant: "destructive" }); } } catch (error) { toast({ title: "Erro", description: "Falha na comunicação com o servidor", variant: "destructive" }); } finally { setLoading(false); } }; const handleBindEmpresa = async () => { if (!selectedEmpresa || !bindPlusId) return; try { const res = await fetch(`/api/retail/plus/empresas/${selectedEmpresa}/bind`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ plusEmpresaId: parseInt(bindPlusId) }), }); if (res.ok) { toast({ title: "Empresa vinculada", description: "Empresa vinculada ao Plus com sucesso" }); loadEmpresas(); setBindPlusId(""); } } catch (error) { toast({ title: "Erro", description: "Falha ao vincular empresa", variant: "destructive" }); } }; const currentEmpresa = empresas.find(e => e.id.toString() === selectedEmpresa); const SyncResultBadge = ({ result }: { result?: SyncResult }) => { if (!result) return null; return (
{result.success ? ( OK: {result.created || 0} criados, {result.updated || 0} atualizados ) : ( {result.message || "Falha"} )}
); }; return (

Integração Plus ERP

Sincronização bidirecional com o Arcádia Plus (Laravel ERP)

{plusStatus && ( {plusStatus.connected ? ( <> Conectado ) : ( <> Desconectado )} )}
Empresa Ativa Selecione a empresa para operações de sincronização
{currentEmpresa && (
{currentEmpresa.razaoSocial} {currentEmpresa.tipo === "matriz" ? "Matriz" : "Filial"}
CNPJ: {currentEmpresa.cnpj}
Plus ID: {currentEmpresa.plusEmpresaId || "Não vinculada"} {!currentEmpresa.plusEmpresaId && (
setBindPlusId(e.target.value)} className="h-7 w-36 text-xs" data-testid="input-bind-plus-id" />
)}
)}
Clientes / Fornecedores
Produtos / Estoque
Vendas PDV

As vendas são sincronizadas automaticamente ao finalizar no PDV. Use o botão abaixo para reenviar vendas pendentes.

Documentos Fiscais

Emissão de NF-e e NFC-e via Plus ERP. Selecione a venda na aba PDV e use a opção "Emitir NF-e" para gerar o documento fiscal.

Integrado via Plus API
); }