Spaces:
Running
Running
File size: 7,040 Bytes
188bf54 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | 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' };
|