restaurante / frontend /src /components /admin /CRMManager.jsx
dimensionalpulsar's picture
Upload 51 files
188bf54 verified
import React, { useState, useEffect } from 'react';
import { db } from '../../firebase/config';
import { ref, onValue } from 'firebase/database';
import { Users, Truck, Heart, Star, Phone, MapPin, History, Search } from 'lucide-react';
export default function CRMManager() {
const [customers, setCustomers] = useState([]);
const [deliveryOrders, setDeliveryOrders] = useState([]);
const [searchTerm, setSearchTerm] = useState('');
useEffect(() => {
// Analytics for CRM (from Orders)
onValue(ref(db, 'orders'), (snapshot) => {
const data = snapshot.val();
if (data) {
const orders = Object.values(data);
const customerMap = {};
const deliveries = [];
orders.forEach(order => {
if (order.customerEmail) {
const email = order.customerEmail;
if (!customerMap[email]) {
customerMap[email] = {
email,
name: order.customerName || 'Cliente',
totalSpent: 0,
orderCount: 0,
lastOrder: 0
};
}
customerMap[email].totalSpent += order.total;
customerMap[email].orderCount += 1;
if (order.timestamp > customerMap[email].lastOrder) {
customerMap[email].lastOrder = order.timestamp;
}
}
if (order.type === 'delivery') {
deliveries.push(order);
}
});
setCustomers(Object.values(customerMap).sort((a,b) => b.totalSpent - a.totalSpent));
setDeliveryOrders(deliveries.sort((a,b) => b.timestamp - a.timestamp));
}
});
}, []);
const filteredCustomers = customers.filter(c =>
c.email.toLowerCase().includes(searchTerm.toLowerCase()) ||
c.name.toLowerCase().includes(searchTerm.toLowerCase())
);
return (
<div className="animate-fade-in" style={{ padding: '0 1rem' }}>
<header style={{ marginBottom: '2.5rem' }}>
<h2 className="text-gradient" style={{ fontSize: '2rem', fontWeight: '800', display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
<Users size={28} /> CRM & Fidelización
</h2>
<p style={{ color: 'var(--text-muted)' }}>Gestión de clientes frecuentes y seguimiento de delivery</p>
</header>
<div style={{ display: 'grid', gridTemplateColumns: '1.2fr 1fr', gap: '2rem' }}>
{/* Customer List */}
<section className="glass-card">
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1.5rem' }}>
<h3 style={sectionTitleStyle}><Heart size={18} /> Clientes Frecuentes</h3>
<div style={{ position: 'relative', width: '200px' }}>
<Search size={14} style={{ position: 'absolute', left: '10px', top: '12px', color: 'var(--text-muted)' }} />
<input
type="text" placeholder="Buscar..."
value={searchTerm} onChange={e => setSearchTerm(e.target.value)}
style={{ ...inputStyle, paddingLeft: '30px', padding: '0.4rem 0.5rem 0.4rem 2rem', fontSize: '0.8rem' }}
/>
</div>
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem', maxHeight: '500px', overflowY: 'auto' }}>
{filteredCustomers.map((c, i) => (
<div key={c.email} className="glass-panel" style={{ padding: '1rem', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
<div style={{ width: '40px', height: '40px', background: 'rgba(0,0,0,0.03)', borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: '800', color: i < 3 ? 'var(--warning)' : 'var(--text-muted)' }}>
{i < 3 ? <Star size={18} fill="var(--warning)" /> : i + 1}
</div>
<div>
<div style={{ fontWeight: '700' }}>{c.name}</div>
<div style={{ fontSize: '0.75rem', color: 'var(--text-muted)' }}>{c.email}</div>
</div>
</div>
<div style={{ textAlign: 'right' }}>
<div style={{ color: 'var(--success)', fontWeight: '800' }}>${c.totalSpent.toFixed(2)}</div>
<div style={{ fontSize: '0.65rem', color: 'var(--text-muted)' }}>{c.orderCount} visitas | {Math.floor(c.totalSpent / 10)} pts</div>
</div>
</div>
))}
</div>
</section>
{/* Delivery Activity */}
<section className="glass-card">
<h3 style={sectionTitleStyle}><Truck size={18} /> Monitor de Delivery</h3>
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
{deliveryOrders.slice(0, 10).map(order => (
<div key={order.id} style={{ padding: '1rem', borderLeft: '3px solid var(--primary)', background: 'rgba(0,0,0,0.02)', borderRadius: '0 8px 8px 0' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '0.5rem' }}>
<span style={{ fontWeight: '700', fontSize: '0.9rem' }}>#{order.id.slice(-5)} - {order.customerName}</span>
<span style={{ fontSize: '0.75rem', padding: '2px 8px', borderRadius: '4px', background: 'rgba(255,90,95,0.1)', color: 'var(--primary)' }}>{order.status.toUpperCase()}</span>
</div>
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', fontSize: '0.75rem', color: 'var(--text-muted)' }}>
<MapPin size={12} /> {order.address || 'Local / Para llevar'}
</div>
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', fontSize: '0.75rem', color: 'var(--text-muted)', marginTop: '0.25rem' }}>
<History size={12} /> {new Date(order.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</div>
</div>
))}
{deliveryOrders.length === 0 && <p style={{ textAlign: 'center', color: 'var(--text-muted)', paddingTop: '2rem' }}>No hay pedidos delivery recientes</p>}
</div>
</section>
</div>
</div>
);
}
const inputStyle = { width: '100%', padding: '0.8rem', borderRadius: '8px', background: 'rgba(0,0,0,0.03)', border: '1px solid var(--border-subtle)', color: 'var(--text-main)', outline: 'none' };
const sectionTitleStyle = { fontSize: '1.2rem', fontWeight: '700', display: 'flex', alignItems: 'center', gap: '0.75rem' };