import React, { useEffect, useState } from "react"; import { collection, query, where, onSnapshot, Timestamp, addDoc, setDoc, doc, serverTimestamp } from "firebase/firestore"; import { db } from "../../firebase"; import { useAuth } from "../../context/AuthContext"; import { negocioService, type Negocio } from "../../services/negocioService"; import { toast } from "react-hot-toast"; import { UtensilsCrossed, CalendarCheck, ShoppingBag, DollarSign, QrCode, Monitor, BookOpen, Package, FileText, CreditCard, Receipt, Brain, Bot, ExternalLink, ChevronRight, } from "lucide-react"; interface KPIs { comandasActivas: number; reservasHoy: number; ticketsPendientes: number; ventasHoy: number; } const MODULOS = [ { id: "pos", emoji: "💳", label: "Punto de venta", desc: "Cobra rápido con imágenes", href: "/restaurante/pos", from: "#16a34a", to: "#22c55e" }, { id: "comandas", emoji: "🍳", label: "Comandas", desc: "Pantalla de cocina en tiempo real", href: "/restaurante/comandas", from: "#ea580c", to: "#f97316" }, { id: "menu_qr", emoji: "📱", label: "Menú & QR", desc: "Edita la carta y genera QR", href: "/restaurante/menu", from: "#0284c7", to: "#0ea5e9" }, { id: "inventario", emoji: "📦", label: "Inventario", desc: "Stock, imágenes y alertas", href: "/restaurante/inventario", from: "#7c3aed", to: "#a855f7" }, { id: "gastos", emoji: "📊", label: "Gastos", desc: "Registra y analiza costos", href: "/restaurante/gastos", from: "#dc2626", to: "#f87171" }, { id: "reservas", emoji: "📅", label: "Reservas", desc: "Mesas y reservaciones", href: "/restaurante/reservas", from: "#0891b2", to: "#22d3ee" }, { id: "gerente_ia", emoji: "🧠", label: "Gerente IA", desc: "Análisis inteligente del negocio", href: "/restaurante/gerente", from: "#4f46e5", to: "#818cf8" }, { id: "compras", emoji: "🛒", label: "Agrovida Foods", desc: "Insumos con descuento 20%", href: "/restaurante/compras", from: "#d97706", to: "#fbbf24" }, { id: "facturacion",emoji: "🧾", label: "Facturación DIAN", desc: "Facturas con sello DIAN", href: "/restaurante/facturacion", from: "#475569", to: "#64748b" }, { id: "meta_ads", emoji: "📣", label: "Meta Ads IA", desc: "Lanza campañas en Facebook e Instagram con IA", href: "/negocio/meta-ads", from: "#1877f2", to: "#0a52c4" }, ]; // ── Pantalla de recuperación para registros incompletos ────────────────────── function CompletarRegistro({ uid, email, nombre }: { uid: string; email: string; nombre: string }) { const [restaurante, setRestaurante] = useState(""); const [telefono, setTelefono] = useState(""); const [loading, setLoading] = useState(false); async function handleCompletar(e: React.FormEvent) { e.preventDefault(); if (!restaurante.trim()) { toast.error("Escribe el nombre del establecimiento"); return; } setLoading(true); try { const modulos = ["reservas", "menu_qr", "comandas", "pos", "finanzas", "inventario", "gastos"]; const negRef = await addDoc(collection(db, "negocios"), { nombre: restaurante.trim(), tipo: "restaurante", ownerUid: uid, ownerId: uid, ownerNombre: nombre, ownerEmail: email.toLowerCase(), telefono: telefono.trim(), ciudad: "", suscripcion: { status: "trial", precioMensualCOP: 90000, periodoActual: new Date().toISOString().slice(0, 7), renovacion: new Date(Date.now() + 7 * 86400000).toISOString(), historialPagos: [], }, modulosActivos: modulos, creadoEn: serverTimestamp(), }); await setDoc(doc(db, "users", uid), { negocioId: negRef.id, rolNegocio: "owner", negocioTipo: "restaurante", modulosAutorizados: modulos, }, { merge: true }); toast.success("¡Registro completado! Recargando…"); setTimeout(() => window.location.reload(), 1200); } catch (err: any) { toast.error("Error al completar: " + (err.message || "intenta de nuevo")); } finally { setLoading(false); } } return (
🍽️
Completa tu registro
Tu cuenta existe pero falta el nombre del establecimiento
setRestaurante(e.target.value)} placeholder="Ej: La Hacienda, Brioche, El Rincón…" required style={{ width: "100%", height: 48, borderRadius: 12, border: "1.5px solid #e5e7eb", padding: "0 14px", fontSize: 14, outline: "none", boxSizing: "border-box" }} />
setTelefono(e.target.value)} placeholder="3001234567" style={{ width: "100%", height: 48, borderRadius: 12, border: "1.5px solid #e5e7eb", padding: "0 14px", fontSize: 14, outline: "none", boxSizing: "border-box" }} />
); } export const RestauranteDashboard: React.FC = () => { const { profile } = useAuth(); // Admin puede pasar ?negocioId=xxx para ver el panel de otro negocio const urlNegocioId = new URLSearchParams(window.location.search).get("negocioId"); const negocioId = urlNegocioId || profile?.negocioId || ""; const [negocio, setNegocio] = useState(null); const [kpis, setKpis] = useState({ comandasActivas: 0, reservasHoy: 0, ticketsPendientes: 0, ventasHoy: 0 }); // Cargar el negocio para obtener su nombre real useEffect(() => { if (!negocioId) return; negocioService.getNegocio(negocioId).then(setNegocio).catch(() => {}); }, [negocioId]); useEffect(() => { if (!negocioId) return; const unsubs: (() => void)[] = []; unsubs.push(onSnapshot( query(collection(db, "negocio_comandas"), where("negocioId", "==", negocioId), where("estado", "in", ["pendiente", "preparando"])), snap => setKpis(k => ({ ...k, comandasActivas: snap.size })) )); const hoyStr = new Date().toISOString().slice(0, 10); unsubs.push(onSnapshot( query(collection(db, "negocio_reservas"), where("negocioId", "==", negocioId), where("fecha", "==", hoyStr)), snap => setKpis(k => ({ ...k, reservasHoy: snap.size })) )); unsubs.push(onSnapshot( query(collection(db, "negocio_tickets"), where("negocioId", "==", negocioId), where("estado", "==", "pendiente_pago")), snap => setKpis(k => ({ ...k, ticketsPendientes: snap.size })) )); const inicioHoy = Timestamp.fromDate((() => { const d = new Date(); d.setHours(0,0,0,0); return d; })()); const finHoy = Timestamp.fromDate((() => { const d = new Date(); d.setHours(23,59,59,999); return d; })()); unsubs.push(onSnapshot( query(collection(db, "negocio_tickets"), where("negocioId", "==", negocioId), where("estado", "==", "pagado"), where("creadoEn", ">=", inicioHoy), where("creadoEn", "<=", finHoy)), snap => setKpis(k => ({ ...k, ventasHoy: snap.docs.reduce((a, d) => a + (d.data().total ?? 0), 0) })) )); return () => unsubs.forEach(u => u()); }, [negocioId]); const nombreRestaurante = negocio?.nombre ?? (profile as any)?.nombreNegocio ?? "Mi Restaurante"; const hora = new Date().getHours(); const saludo = hora < 12 ? "Buenos días" : hora < 18 ? "Buenas tardes" : "Buenas noches"; // Usuario registrado pero sin negocio creado → pantalla de recuperación if (!urlNegocioId && profile && !profile.negocioId && profile.rolNegocio === "owner") { return ( ); } return (
{/* ── HERO HEADER ── */}
{/* Orbes */}
🍽️ {saludo}

{nombreRestaurante}

{new Date().toLocaleDateString("es-CO", { weekday: "long", day: "numeric", month: "long" })}

{/* KPIs dentro del header */}
{[ { emoji: "🍳", label: "Comandas", value: kpis.comandasActivas, accent: "#f97316" }, { emoji: "📅", label: "Reservas hoy", value: kpis.reservasHoy, accent: "#38bdf8" }, { emoji: "🎫", label: "Pendientes", value: kpis.ticketsPendientes, accent: "#fbbf24" }, { emoji: "💰", label: "Ventas hoy", value: `$${(kpis.ventasHoy/1000).toFixed(0)}k`, accent: "#4ade80" }, ].map((k, i) => (
{k.emoji}
{k.value}
{k.label}
))}
{/* ── ACCESO RÁPIDO menú público ── */} {negocioId && ( 🔗
Menú público
agrovidacol.com/m/{negocioId}
)} {/* ── MÓDULOS ── */}
Módulos
{/* ── BANNER MESERO IA ── */}
🤖
Mesero IA activado LIVE

Tus clientes acceden al menú con QR → chatean con el Mesero IA → el pedido aparece automáticamente en Comandas y en Caja.

{negocioId && ( Ver chat del cliente )}
{/* Banner suscripción */}
); }; const SubscriptionBanner: React.FC<{ negocioId: string }> = ({ negocioId }) => { const [estado, setEstado] = useState("trial"); const [diasRestantes, setDiasRestantes] = useState(7); useEffect(() => { if (!negocioId) return; const unsub = onSnapshot(collection(db, "negocios"), snap => { const d = snap.docs.find(d => d.id === negocioId); if (d) { const data = d.data(); setEstado(data.suscripcion?.estado ?? "trial"); if (data.suscripcion?.trialFin) { const fin = data.suscripcion.trialFin.toDate ? data.suscripcion.trialFin.toDate() : new Date(data.suscripcion.trialFin); setDiasRestantes(Math.max(0, Math.ceil((fin.getTime() - Date.now()) / 86400000))); } } }); return unsub; }, [negocioId]); if (estado === "activa") return null; return (
{estado === "trial" ? `Prueba gratis · ${diasRestantes} días restantes` : "Suscripción inactiva"}

Activa tu suscripción para continuar usando todos los módulos sin límites.

); };