import { BrowserFrame } from "@/components/Browser/BrowserFrame"; import { SupersetDashboard } from "@/components/SupersetDashboard"; import { useState, useEffect } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Badge } from "@/components/ui/badge"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription } from "@/components/ui/dialog"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Textarea } from "@/components/ui/textarea"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Switch } from "@/components/ui/switch"; import { useToast } from "@/hooks/use-toast"; import { useAuth } from "@/hooks/use-auth"; import { Package, Users, Truck, ShoppingCart, FileText, Plus, Search, Edit, Trash2, RefreshCw, Receipt, BookOpen, ArrowRight, CheckCircle, XCircle, Clock, DollarSign, BarChart3, Building2, Package2, TrendingUp, Cloud, CloudOff, Database, Loader2, Target, Briefcase, Calculator, FolderKanban, UserCog, AlertTriangle, Pencil, Smartphone, Wallet, ExternalLink } from "lucide-react"; import { useLocation } from "wouter"; import { useSoeMotor, SoeMotor } from "@/contexts/SoeMotorContext"; type TabType = "dashboard" | "persons" | "customers" | "suppliers" | "products" | "sales" | "purchases" | "crm" | "accounting" | "projects" | "hr" | "config" | "sync"; interface Customer { id: number; code: string; name: string; type: string; taxId: string; email: string; phone: string; city: string; state: string; status: string; creditLimit: string; } interface Supplier { id: number; code: string; name: string; taxId: string; email: string; phone: string; city: string; state: string; status: string; } interface Product { id: number; code: string; name: string; description: string; category: string; unit: string; costPrice: string; salePrice: string; stockQty: string; minStock: string; ncm: string; barcode?: string; taxGroupId?: number; taxGroupName?: string; status: string; requiresSerialTracking?: boolean; trackingType?: string; defaultBrand?: string; defaultModel?: string; } interface TaxGroup { id: number; nome: string; ncm: string; cstIcms: string; aliqIcms: string; } interface SalesOrder { id: number; orderNumber: string; customerId: number; customerName?: string; orderDate: string; deliveryDate: string; status: string; subtotal: string; discount: string; tax: string; total: string; } interface PurchaseOrder { id: number; orderNumber: string; supplierId: number; supplierName?: string; orderDate: string; expectedDate: string; status: string; subtotal: string; total: string; } interface ERPNextStatus { connected: boolean; message: string; user?: string; url?: string; } interface ERPNextCustomer { name: string; customer_name: string; customer_type: string; customer_group?: string; territory?: string; tax_id?: string; email_id?: string; mobile_no?: string; disabled?: number; } interface ERPNextSupplier { name: string; supplier_name: string; supplier_type?: string; supplier_group?: string; tax_id?: string; email_id?: string; mobile_no?: string; disabled?: number; } interface ERPNextItem { name: string; item_code: string; item_name: string; item_group?: string; stock_uom?: string; description?: string; standard_rate?: number; valuation_rate?: number; disabled?: number; } interface ERPNextSalesOrder { name: string; customer: string; customer_name?: string; transaction_date: string; delivery_date?: string; status: string; grand_total: number; net_total: number; } interface ERPNextLead { name: string; lead_name: string; company_name?: string; email_id?: string; mobile_no?: string; status: string; source?: string; } interface ERPNextOpportunity { name: string; party_name: string; opportunity_type?: string; status: string; expected_closing?: string; opportunity_amount?: number; } interface ErpSegment { id: number; code: string; name: string; category: string; description?: string; modules?: string[]; features?: Record; } interface ErpConfigData { id?: number; tenantId?: number; segmentId?: number; companyName?: string; tradeName?: string; taxId?: string; stateRegistration?: string; cityRegistration?: string; taxRegime?: string; modulesCrm?: number; modulesSales?: number; modulesPurchases?: number; modulesStock?: number; modulesFinance?: number; modulesAccounting?: number; modulesProduction?: number; modulesProjects?: number; modulesHr?: number; modulesServiceOrder?: number; } const api = { get: async (url: string) => { const res = await fetch(url, { credentials: "include" }); if (!res.ok) throw new Error("Request failed"); return res.json(); }, post: async (url: string, data: any) => { const res = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), credentials: "include", }); if (!res.ok) throw new Error("Request failed"); return res.json(); }, put: async (url: string, data: any) => { const res = await fetch(url, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), credentials: "include", }); if (!res.ok) throw new Error("Request failed"); return res.json(); }, delete: async (url: string) => { const res = await fetch(url, { method: "DELETE", credentials: "include" }); if (!res.ok) throw new Error("Request failed"); return res.json(); }, }; function PersonsTab() { const { toast } = useToast(); const [persons, setPersons] = useState([]); const [loading, setLoading] = useState(true); const [search, setSearch] = useState(""); const [roleFilter, setRoleFilter] = useState("all"); const [showDialog, setShowDialog] = useState(false); const [editingPerson, setEditingPerson] = useState(null); const [formData, setFormData] = useState({ fullName: "", cpfCnpj: "", email: "", phone: "", whatsapp: "", address: "", city: "", state: "", zipCode: "", notes: "", roles: [] as string[] }); const loadPersons = async () => { try { setLoading(true); let url = "/api/soe/persons"; const params = new URLSearchParams(); if (search) params.append("search", search); if (roleFilter && roleFilter !== "all") params.append("role", roleFilter); if (params.toString()) url += `?${params.toString()}`; const res = await fetch(url, { credentials: "include" }); if (res.ok) { const data = await res.json(); setPersons(data); } } catch (error) { console.error("Error loading persons:", error); } finally { setLoading(false); } }; useEffect(() => { loadPersons(); }, [search, roleFilter]); const handleEdit = (person: any) => { setEditingPerson(person); setFormData({ fullName: person.fullName || "", cpfCnpj: person.cpfCnpj || "", email: person.email || "", phone: person.phone || "", whatsapp: person.whatsapp || "", address: person.address || "", city: person.city || "", state: person.state || "", zipCode: person.zipCode || "", notes: person.notes || "", roles: Array.isArray(person.roles) ? (typeof person.roles[0] === 'string' ? person.roles : person.roles.map((r: any) => r.roleType || r)) : [] }); setShowDialog(true); }; const handleSave = async () => { try { const method = editingPerson ? "PUT" : "POST"; const url = editingPerson ? `/api/soe/persons/${editingPerson.id}` : "/api/soe/persons"; const res = await fetch(url, { method, headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ...formData, roles: formData.roles }), credentials: "include" }); if (res.ok) { toast({ title: editingPerson ? "Pessoa atualizada!" : "Pessoa cadastrada!" }); setShowDialog(false); setEditingPerson(null); setFormData({ fullName: "", cpfCnpj: "", email: "", phone: "", whatsapp: "", address: "", city: "", state: "", zipCode: "", notes: "", roles: [] }); loadPersons(); } else { toast({ title: "Erro ao salvar pessoa", variant: "destructive" }); } } catch (error) { toast({ title: "Erro ao salvar pessoa", variant: "destructive" }); } }; const toggleRole = (role: string) => { setFormData(prev => ({ ...prev, roles: prev.roles.includes(role) ? prev.roles.filter(r => r !== role) : [...prev.roles, role] })); }; const roleLabels: Record = { customer: "Cliente", supplier: "Fornecedor", employee: "Funcionário", technician: "Técnico", partner: "Parceiro" }; return ( <>
setSearch(e.target.value)} />
Nome CPF/CNPJ Telefone Cidade/UF Papéis Ações {loading ? ( ) : persons.length === 0 ? ( Nenhuma pessoa cadastrada ) : ( persons.map((person) => ( {person.fullName} {person.cpfCnpj} {person.phone || person.whatsapp} {person.city}/{person.state}
{(Array.isArray(person.roles) ? person.roles : []).map((role: any, idx: number) => { const roleType = typeof role === 'string' ? role : role.roleType; return {roleLabels[roleType] || roleType}; })}
)) )}
{editingPerson ? "Editar Pessoa" : "Nova Pessoa"} Cadastro unificado de pessoas - uma pessoa pode ter múltiplos papéis
setFormData({...formData, fullName: e.target.value})} />
setFormData({...formData, cpfCnpj: e.target.value})} />
setFormData({...formData, email: e.target.value})} />
setFormData({...formData, phone: e.target.value})} />
setFormData({...formData, city: e.target.value})} />
setFormData({...formData, state: e.target.value})} />
setFormData({...formData, zipCode: e.target.value})} />
{Object.entries(roleLabels).map(([key, label]) => ( ))}