338 lines
29 KiB
TypeScript
338 lines
29 KiB
TypeScript
"use client";
|
||
|
||
import { useState, useEffect, useMemo } from "react";
|
||
import { motion } from "framer-motion";
|
||
|
||
type LeadStatus = "new" | "contacted" | "qualified" | "proposal" | "won" | "lost";
|
||
|
||
interface Lead {
|
||
id: string;
|
||
name: string;
|
||
category: "restaurant" | "real-estate" | "clinic" | "car-rental" | "hp-client" | "other";
|
||
phone: string;
|
||
email: string;
|
||
website: string;
|
||
address: string;
|
||
rating: number;
|
||
score: number;
|
||
notes: string;
|
||
status: LeadStatus;
|
||
lastContact: string;
|
||
nextAction: string;
|
||
createdAt: string;
|
||
}
|
||
|
||
const allLeads: Lead[] = [
|
||
// New leads from research Feb 17
|
||
{ id: "r11", name: "Restaurante Milan", category: "restaurant", phone: "+34 952 44 58 55", email: "", website: "", address: "Av. Federico Garcia Lorca 7, 29630 Benalmádena", rating: 0, score: 8, notes: "Italian restaurant - call for AI demo", status: "new", lastContact: "", nextAction: "Call demo", createdAt: "2026-02-17" },
|
||
{ id: "r12", name: "Tex Mex Gringos", category: "restaurant", phone: "", email: "reservas@restaurantespuertomarina.com", website: "restaurantespuertomarina.com", address: "Puerto Marina, Benalmádena", rating: 0, score: 8, notes: "Email found - send AI proposal", status: "new", lastContact: "", nextAction: "Email intro", createdAt: "2026-02-17" },
|
||
{ id: "r13", name: "Lime & Lemon Tapas", category: "restaurant", phone: "", email: "info@limeandlemonbenalmadena.com", website: "limeandlemonbenalmadena.com", address: "Av. Las Palmeras 1, 29630 Benalmádena", rating: 0, score: 8, notes: "Email found - send AI proposal", status: "new", lastContact: "", nextAction: "Email intro", createdAt: "2026-02-17" },
|
||
{ id: "r14", name: "El Parador", category: "restaurant", phone: "+34 952 44 92 93", email: "", website: "", address: "Av. Juan Luis Peralta 47, 29639 Benalmádena", rating: 4.0, score: 8, notes: "Call for AI demo", status: "new", lastContact: "", nextAction: "Call demo", createdAt: "2026-02-17" },
|
||
{ id: "r15", name: "Escorpio Restaurante", category: "restaurant", phone: "+34 952 569 047", email: "", website: "", address: "Santo Domingo de Guzmán 7, Benalmádena", rating: 0, score: 8, notes: "Call for AI demo", status: "new", lastContact: "", nextAction: "Call demo", createdAt: "2026-02-17" },
|
||
{ id: "r16", name: "The Bull Bar", category: "restaurant", phone: "+34 646 569 374", email: "", website: "", address: "Av del Chorrillo 15, Benalmádena", rating: 0, score: 8, notes: "Call for AI demo", status: "new", lastContact: "", nextAction: "Call demo", createdAt: "2026-02-17" },
|
||
{ id: "r17", name: "La Plaza Restaurant", category: "restaurant", phone: "+34 952 44 84 83", email: "", website: "", address: "Plaza de Espana 2, 29639 Benalmádena", rating: 4.3, score: 8, notes: "Call for AI demo", status: "new", lastContact: "", nextAction: "Call demo", createdAt: "2026-02-17" },
|
||
{ id: "r18", name: "Caliu Restaurant", category: "restaurant", phone: "", email: "caliu.torremolinos@gmail.com", website: "", address: "Torremolinos", rating: 0, score: 7, notes: "Email found - send proposal", status: "new", lastContact: "", nextAction: "Email intro", createdAt: "2026-02-17" },
|
||
{ id: "r19", name: "The Carvery", category: "restaurant", phone: "", email: "info@thecarverycompany.com", website: "", address: "Benalmádena", rating: 0, score: 7, notes: "Email found - send proposal", status: "new", lastContact: "", nextAction: "Email intro", createdAt: "2026-02-17" },
|
||
{ id: "re6", name: "Engel & Völkers Costa del Sol", category: "real-estate", phone: "+34 952 650 234", email: "", website: "", address: "CC Diana Local 23, 29688 Estepona", rating: 0, score: 8, notes: "Premium agency - offer AI employee", status: "new", lastContact: "", nextAction: "Call demo", createdAt: "2026-02-17" },
|
||
{ id: "re7", name: "Your Viva Marbella", category: "real-estate", phone: "+34 951 27 27 43", email: "", website: "", address: "CC El Rosario, 29604 Marbella", rating: 0, score: 8, notes: "Premium agency - offer AI employee", status: "new", lastContact: "", nextAction: "Call demo", createdAt: "2026-02-17" },
|
||
{ id: "re8", name: "Panorama Properties", category: "real-estate", phone: "+34 952 774 266", email: "", website: "", address: "Hotel Local 23, 29602 Marbella", rating: 0, score: 8, notes: "Premium agency - offer AI employee", status: "new", lastContact: "", nextAction: "Call demo", createdAt: "2026-02-17" },
|
||
{ id: "re9", name: "Marbella For Sale", category: "real-estate", phone: "+34 952 907 386", email: "", website: "", address: "Edif. Marina Banús, 29660 Puerto Banús", rating: 0, score: 8, notes: "Premium agency - offer AI employee", status: "new", lastContact: "", nextAction: "Call demo", createdAt: "2026-02-17" },
|
||
{ id: "re10", name: "Domus Venari", category: "real-estate", phone: "+34 952 444 295", email: "", website: "", address: "Ctra. N340 KM189, 29604 Marbella", rating: 0, score: 8, notes: "Premium agency - offer AI employee", status: "new", lastContact: "", nextAction: "Call demo", createdAt: "2026-02-17" },
|
||
{ id: "re11", name: "Diana Morales Properties", category: "real-estate", phone: "+34 952 765 138", email: "", website: "", address: "Av. Cánovas del Castillo 4, 29601 Marbella", rating: 0, score: 8, notes: "Premium agency - offer AI employee", status: "new", lastContact: "", nextAction: "Call demo", createdAt: "2026-02-17" },
|
||
{ id: "re12", name: "Hacienda Estates", category: "real-estate", phone: "+34 952 850 154", email: "", website: "", address: "CC Pinogolf Local 2, 29604 Elviria", rating: 0, score: 7, notes: "Premium agency - offer AI employee", status: "new", lastContact: "", nextAction: "Call demo", createdAt: "2026-02-17" },
|
||
{ id: "re13", name: "Sun Med Estates", category: "real-estate", phone: "+34 952 493 372", email: "", website: "", address: "c/ Sedella 3, La Cala de Mijas", rating: 0, score: 7, notes: "Premium agency - offer AI employee", status: "new", lastContact: "", nextAction: "Call demo", createdAt: "2026-02-17" },
|
||
{ id: "c4", name: "Smart Dental (new)", category: "clinic", phone: "+34 911 98 04 65", email: "", website: "", address: "Av. Blas Infante 17, 29631 Benalmádena", rating: 0, score: 9, notes: "Call for AI demo", status: "new", lastContact: "", nextAction: "Call demo", createdAt: "2026-02-17" },
|
||
{ id: "c5", name: "Grupo Dental Clinics", category: "clinic", phone: "", email: "", website: "", address: "Av. Cdad. de Melilla 26, 29639 Benalmádena", rating: 0, score: 7, notes: "Find contact info", status: "new", lastContact: "", nextAction: "Find phone", createdAt: "2026-02-17" },
|
||
{ id: "cr3", name: "Marbesol Car Rental", category: "car-rental", phone: "+34 952 93 44 12", email: "", website: "", address: "Málaga Airport", rating: 0, score: 7, notes: "Offer AI employee", status: "new", lastContact: "", nextAction: "Call demo", createdAt: "2026-02-17" },
|
||
// Original leads
|
||
{ id: "r1", name: "Tex Mex Gringos", category: "restaurant", phone: "+34 951 777 848", email: "info@restaurantespuertomarina.com", website: "restaurantespuertomarina.com", address: "Calle La Fragata, s/n, 29630 Benalmádena", rating: 4.5, score: 7, notes: "Has website - offer AI employee 24/7", status: "new", lastContact: "", nextAction: "Call - offer AI employee", createdAt: "2026-02-16" },
|
||
{ id: "r2", name: "La Mar Chica", category: "restaurant", phone: "+34 951 634 708", email: "info.lamarchica@gmail.com", website: "mar-chica.com", address: "Calle Marbella, 1, 29639 Benalmádena Pueblo", rating: 4.3, score: 7, notes: "Has website - offer AI employee 24/7", status: "new", lastContact: "", nextAction: "Call - offer AI employee", createdAt: "2026-02-16" },
|
||
{ id: "r3", name: "SALU Grill & Wine", category: "restaurant", phone: "+34 951 715 736", email: "salu.spain@gmail.com", website: "salu-restaurant.com", address: "Calle San José, 6, Benalmádena Pueblo", rating: 4.6, score: 8, notes: "Good site - offer AI employee upgrade", status: "new", lastContact: "", nextAction: "Schedule demo", createdAt: "2026-02-16" },
|
||
{ id: "r4", name: "Basil", category: "restaurant", phone: "631 971 592", email: "", website: "basilbenalmadena.com", address: "Plaza Nueva Bonanza, 29630 Benalmádena", rating: 4.4, score: 6, notes: "Has WhatsApp - offer AI employee", status: "new", lastContact: "", nextAction: "Call - offer AI employee", createdAt: "2026-02-16" },
|
||
{ id: "r5", name: "Lime & Lemon Tapas", category: "restaurant", phone: "", email: "", website: "limeandlemonbenalmadena.com", address: "Benalmádena", rating: 0, score: 5, notes: "Has website - offer AI employee 24/7", status: "new", lastContact: "", nextAction: "Find phone first", createdAt: "2026-02-16" },
|
||
{ id: "r6", name: "Restaurant No7", category: "restaurant", phone: "+34 655 036 827", email: "", website: "", address: "Benalmádena", rating: 0, score: 10, notes: "NO WEBSITE - BIG OPPORTUNITY", status: "new", lastContact: "", nextAction: "CALL NOW", createdAt: "2026-02-16" },
|
||
{ id: "r7", name: "Capitan Bar & Restaurant", category: "restaurant", phone: "+34 674 591 584", email: "", website: "", address: "Benalmádena", rating: 0, score: 10, notes: "NO WEBSITE - BIG OPPORTUNITY", status: "new", lastContact: "", nextAction: "CALL NOW", createdAt: "2026-02-16" },
|
||
{ id: "r8", name: "TORO Puerto Marina", category: "restaurant", phone: "+34 952 913 177", email: "", website: "restaurantespuertomarina.com/toro-puerto-marina", address: "Puerto Marina, Benalmádena", rating: 4.5, score: 7, notes: "Has website - offer AI employee", status: "new", lastContact: "", nextAction: "Email introduction", createdAt: "2026-02-16" },
|
||
{ id: "r9", name: "Trocadero Benalmádena", category: "restaurant", phone: "+34 681 142 944", email: "", website: "", address: "Avenida del Sol 121, Benalmádena", rating: 4.2, score: 10, notes: "NO WEBSITE - BIG OPPORTUNITY", status: "new", lastContact: "", nextAction: "CALL NOW", createdAt: "2026-02-16" },
|
||
{ id: "r10", name: "Restaurante La Nina", category: "restaurant", phone: "+34 952 449 193", email: "", website: "", address: "Plaza de Espana, Benalmádena Pueblo", rating: 4.4, score: 10, notes: "NO WEBSITE - BIG OPPORTUNITY", status: "new", lastContact: "", nextAction: "CALL NOW", createdAt: "2026-02-16" },
|
||
{ id: "re1", name: "Hernán Bustos Real Estate", category: "real-estate", phone: "", email: "", website: "hernanbustos.com", address: "Benalmádena, Torremolinos", rating: 0, score: 9, notes: "Needs AI employee 24/7", status: "new", lastContact: "", nextAction: "Find contact info", createdAt: "2026-02-16" },
|
||
{ id: "re2", name: "ViVi Real Estate", category: "real-estate", phone: "", email: "", website: "vivi-realestate.com", address: "Costa del Sol", rating: 0, score: 9, notes: "Needs AI employee 24/7", status: "new", lastContact: "", nextAction: "Find contact info", createdAt: "2026-02-16" },
|
||
{ id: "re3", name: "Marbella Mundo", category: "real-estate", phone: "", email: "", website: "marbellamundo.es", address: "Fuengirola, Costa del Sol", rating: 0, score: 9, notes: "Needs AI employee 24/7", status: "new", lastContact: "", nextAction: "Email introduction", createdAt: "2026-02-16" },
|
||
{ id: "re4", name: "Homenetspain", category: "real-estate", phone: "+34 633 300 956", email: "", website: "homenetspain.com", address: "Fuengirola", rating: 0, score: 8, notes: "Needs AI employee 24/7", status: "new", lastContact: "", nextAction: "Call and demo", createdAt: "2026-02-16" },
|
||
{ id: "re5", name: "Costa Listings", category: "real-estate", phone: "", email: "", website: "costalistings.es", address: "Benalmádena", rating: 0, score: 8, notes: "Needs AI employee 24/7", status: "new", lastContact: "", nextAction: "Find contact info", createdAt: "2026-02-16" },
|
||
{ id: "c1", name: "Vithas Xanit Hospital", category: "clinic", phone: "+34 952 367 190", email: "info.xanit@vithas.es", website: "vithas.es", address: "Avenida de los Argonautas, s/n, 29631 Benalmádena", rating: 4.5, score: 8, notes: "BIG opportunity - AI employee", status: "new", lastContact: "", nextAction: "Email introduction", createdAt: "2026-02-16" },
|
||
{ id: "c2", name: "Smart Dental", category: "clinic", phone: "", email: "", website: "", address: "Benalmádena", rating: 4.5, score: 10, notes: "NO WEBSITE - BIG opportunity", status: "new", lastContact: "", nextAction: "Find phone", createdAt: "2026-02-16" },
|
||
{ id: "c3", name: "Rosasco Dental", category: "clinic", phone: "", email: "", website: "", address: "Benalmádena", rating: 4.2, score: 10, notes: "NO WEBSITE - BIG opportunity", status: "new", lastContact: "", nextAction: "Find phone", createdAt: "2026-02-16" },
|
||
{ id: "cr1", name: "Malaga U Drive", category: "car-rental", phone: "", email: "", website: "malagaudrive.com", address: "Malaga/Benalmádena", rating: 0, score: 8, notes: "Needs AI employee 24/7", status: "new", lastContact: "", nextAction: "Email introduction", createdAt: "2026-02-16" },
|
||
{ id: "cr2", name: "ALL IN Car Hire", category: "car-rental", phone: "", email: "", website: "allincarhire.com", address: "Malaga/Benalmádena", rating: 0, score: 8, notes: "Needs AI employee 24/7", status: "new", lastContact: "", nextAction: "Email introduction", createdAt: "2026-02-16" },
|
||
];
|
||
|
||
const categoryIcons: Record<string, string> = {
|
||
restaurant: "🍽️",
|
||
"real-estate": "🏠",
|
||
clinic: "🏥",
|
||
"car-rental": "🚗",
|
||
other: "📌",
|
||
};
|
||
|
||
const statusColors: Record<LeadStatus, string> = {
|
||
new: "bg-blue-500/20 text-blue-400 border-blue-500/30",
|
||
contacted: "bg-yellow-500/20 text-yellow-400 border-yellow-500/30",
|
||
qualified: "bg-purple-500/20 text-purple-400 border-purple-500/30",
|
||
proposal: "bg-orange-500/20 text-orange-400 border-orange-500/30",
|
||
won: "bg-green-500/20 text-green-400 border-green-500/30",
|
||
lost: "bg-red-500/20 text-red-400 border-red-500/30",
|
||
};
|
||
|
||
type SortField = "name" | "category" | "score" | "status" | "phone";
|
||
|
||
export default function LeadsPage() {
|
||
const [leads, setLeads] = useState<Lead[]>(allLeads);
|
||
const [filter, setFilter] = useState<string>("all");
|
||
const [tab, setTab] = useState<"leads" | "email">("leads");
|
||
const [sortField, setSortField] = useState<SortField>("score");
|
||
const [sortAsc, setSortAsc] = useState(false);
|
||
const [selectedLead, setSelectedLead] = useState<Lead | null>(null);
|
||
const [search, setSearch] = useState("");
|
||
|
||
const leadsWithEmail = useMemo(() => leads.filter(l => l.email), [leads]);
|
||
|
||
useEffect(() => {
|
||
const saved = localStorage.getItem("sitemente:leads");
|
||
if (saved) {
|
||
setLeads(JSON.parse(saved));
|
||
} else {
|
||
localStorage.setItem("sitemente:leads", JSON.stringify(allLeads));
|
||
}
|
||
}, []);
|
||
|
||
const saveLeads = (newLeads: Lead[]) => {
|
||
setLeads(newLeads);
|
||
localStorage.setItem("sitemente:leads", JSON.stringify(newLeads));
|
||
};
|
||
|
||
const updateStatus = (id: string, status: LeadStatus) => {
|
||
const updated = leads.map((l) =>
|
||
l.id === id ? { ...l, status, lastContact: new Date().toISOString().split("T")[0] } : l
|
||
);
|
||
saveLeads(updated);
|
||
};
|
||
|
||
const filteredLeads = useMemo(() => {
|
||
return leads
|
||
.filter((l) => {
|
||
if (filter !== "all" && l.category !== filter && l.status !== filter) return false;
|
||
if (search && !l.name.toLowerCase().includes(search.toLowerCase())) return false;
|
||
return true;
|
||
})
|
||
.sort((a, b) => {
|
||
let cmp = 0;
|
||
if (sortField === "name") cmp = a.name.localeCompare(b.name);
|
||
else if (sortField === "category") cmp = a.category.localeCompare(b.category);
|
||
else if (sortField === "score") cmp = b.score - a.score;
|
||
else if (sortField === "status") cmp = a.status.localeCompare(b.status);
|
||
else if (sortField === "phone") cmp = (a.phone ? 1 : 0) - (b.phone ? 1 : 0);
|
||
return sortAsc ? cmp : -cmp;
|
||
});
|
||
}, [leads, filter, sortField, sortAsc, search]);
|
||
|
||
const stats = {
|
||
total: leads.length,
|
||
withPhone: leads.filter(l => l.phone).length,
|
||
noWebsite: leads.filter(l => !l.website).length,
|
||
hot: leads.filter(l => l.score >= 9).length,
|
||
new: leads.filter((l) => l.status === "new").length,
|
||
won: leads.filter((l) => l.status === "won").length,
|
||
};
|
||
|
||
const handleSort = (field: SortField) => {
|
||
if (sortField === field) {
|
||
setSortAsc(!sortAsc);
|
||
} else {
|
||
setSortField(field);
|
||
setSortAsc(false);
|
||
}
|
||
};
|
||
|
||
const SortIcon = ({ field }: { field: SortField }) => (
|
||
<span className="ml-1 opacity-50">{sortField === field ? (sortAsc ? "↑" : "↓") : "↕"}</span>
|
||
);
|
||
|
||
return (
|
||
<div className="min-h-screen bg-[#1a1625] text-white" suppressHydrationWarning>
|
||
<header className="border-b border-white/10 bg-[#1a1625]/90 backdrop-blur sticky top-0 z-50">
|
||
<div className="mx-auto flex w-full max-w-7xl items-center justify-between px-6 py-4">
|
||
<div className="flex items-center gap-4">
|
||
<a href="/" className="flex items-center gap-3">
|
||
<img src="/sitemente-logo-light.png" alt="SiteMente" width={40} height={40} className="h-10 w-auto" />
|
||
<span className="font-bold text-xl">Leads CRM</span>
|
||
</a>
|
||
</div>
|
||
<div className="flex items-center gap-3">
|
||
<a href="/mission-control" className="px-4 py-2 bg-white/10 rounded-lg text-sm hover:bg-white/20 transition">← Mission Control</a>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<main className="mx-auto max-w-7xl px-6 py-8" suppressHydrationWarning>
|
||
<div className="grid grid-cols-2 md:grid-cols-5 gap-4 mb-8">
|
||
<div className="bg-white/5 rounded-xl p-4 border border-white/10">
|
||
<p className="text-2xl font-bold text-brand-pink">{stats.total}</p>
|
||
<p className="text-sm text-white/60">Total Leads</p>
|
||
</div>
|
||
<div className="bg-green-500/10 rounded-xl p-4 border border-green-500/30">
|
||
<p className="text-2xl font-bold text-green-400">{stats.withPhone}</p>
|
||
<p className="text-sm text-white/60">With Phone</p>
|
||
</div>
|
||
<div className="bg-red-500/10 rounded-xl p-4 border border-red-500/30">
|
||
<p className="text-2xl font-bold text-red-400">{stats.noWebsite}</p>
|
||
<p className="text-sm text-white/60">No Website</p>
|
||
</div>
|
||
<div className="bg-orange-500/10 rounded-xl p-4 border border-orange-500/30">
|
||
<p className="text-2xl font-bold text-orange-400">{stats.hot}</p>
|
||
<p className="text-sm text-white/60">Hot (9-10)</p>
|
||
</div>
|
||
<div className="bg-blue-500/10 rounded-xl p-4 border border-blue-500/30">
|
||
<p className="text-2xl font-bold text-blue-400">{stats.won}</p>
|
||
<p className="text-sm text-white/60">Won</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex flex-wrap gap-4 mb-6">
|
||
<div className="flex gap-2 bg-white/10 rounded-lg p-1">
|
||
<button onClick={() => setTab("leads")} className={`px-4 py-2 rounded-lg transition ${tab === "leads" ? "bg-brand-pink text-white" : "hover:bg-white/10"}`}>📋 Leads ({leads.length})</button>
|
||
<button onClick={() => setTab("email")} className={`px-4 py-2 rounded-lg transition ${tab === "email" ? "bg-brand-pink text-white" : "hover:bg-white/10"}`}>✉️ Email Outreach ({leadsWithEmail.length})</button>
|
||
</div>
|
||
{tab === "leads" && (
|
||
<>
|
||
<input type="text" placeholder="Search leads..." value={search} onChange={(e) => setSearch(e.target.value)} className="bg-white/10 border border-white/20 rounded-lg px-4 py-2 min-w-[200px]" />
|
||
<select value={filter} onChange={(e) => setFilter(e.target.value)} className="bg-white/10 border border-white/20 rounded-lg px-4 py-2">
|
||
<option value="all">All ({leads.length})</option>
|
||
<option value="restaurant">Restaurants ({leads.filter(l => l.category === 'restaurant').length})</option>
|
||
<option value="real-estate">Real Estate ({leads.filter(l => l.category === 'real-estate').length})</option>
|
||
<option value="clinic">Clinics ({leads.filter(l => l.category === 'clinic').length})</option>
|
||
<option value="car-rental">Car Rental ({leads.filter(l => l.category === 'car-rental').length})</option>
|
||
</select>
|
||
</>
|
||
)}
|
||
</div>
|
||
|
||
<div className="bg-white/5 rounded-xl border border-white/10 overflow-hidden">
|
||
<div className="overflow-x-auto">
|
||
<table className="w-full">
|
||
<thead className="bg-white/5 border-b border-white/10">
|
||
<tr>
|
||
<th className="text-left px-4 py-3 text-sm font-medium text-white/60 cursor-pointer hover:text-white" onClick={() => handleSort("name")}>Lead <SortIcon field="name" /></th>
|
||
<th className="text-left px-4 py-3 text-sm font-medium text-white/60 cursor-pointer hover:text-white" onClick={() => handleSort("category")}>Category <SortIcon field="category" /></th>
|
||
<th className="text-left px-4 py-3 text-sm font-medium text-white/60 cursor-pointer hover:text-white" onClick={() => handleSort("phone")}>Phone <SortIcon field="phone" /></th>
|
||
<th className="text-left px-4 py-3 text-sm font-medium text-white/60 cursor-pointer hover:text-white" onClick={() => handleSort("score")}>Score <SortIcon field="score" /></th>
|
||
<th className="text-left px-4 py-3 text-sm font-medium text-white/60 cursor-pointer hover:text-white" onClick={() => handleSort("status")}>Status <SortIcon field="status" /></th>
|
||
<th className="text-left px-4 py-3 text-sm font-medium text-white/60">Actions</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{filteredLeads.map((lead) => (
|
||
<motion.tr key={lead.id} initial={{ opacity: 0 }} animate={{ opacity: 1 }} className="border-b border-white/5 hover:bg-white/5">
|
||
<td className="px-4 py-3">
|
||
<p className="font-medium">{lead.name}</p>
|
||
<p className="text-xs text-white/50 truncate max-w-[200px]">{lead.address}</p>
|
||
</td>
|
||
<td className="px-4 py-3"><span className="text-lg">{categoryIcons[lead.category]}</span></td>
|
||
<td className="px-4 py-3">
|
||
{lead.phone ? <a href={"tel:" + lead.phone} className="text-green-400 hover:underline">{lead.phone}</a> : <span className="text-red-400 text-xs">No phone</span>}
|
||
</td>
|
||
<td className="px-4 py-3">
|
||
<span className={"inline-flex items-center justify-center w-8 h-8 rounded-full text-sm font-bold " + (lead.score >= 9 ? "bg-green-500/20 text-green-400" : lead.score >= 7 ? "bg-yellow-500/20 text-yellow-400" : "bg-red-500/20 text-red-400")}>{lead.score}</span>
|
||
</td>
|
||
<td className="px-4 py-3">
|
||
<select value={lead.status} onChange={(e) => updateStatus(lead.id, e.target.value as LeadStatus)} className={"text-xs px-2 py-1 rounded-full border " + statusColors[lead.status as LeadStatus]}>
|
||
<option value="new">New</option>
|
||
<option value="contacted">Contacted</option>
|
||
<option value="qualified">Qualified</option>
|
||
<option value="proposal">Proposal</option>
|
||
<option value="won">Won</option>
|
||
<option value="lost">Lost</option>
|
||
</select>
|
||
</td>
|
||
<td className="px-4 py-3">
|
||
<div className="flex gap-1">
|
||
{lead.phone && <a href={"tel:" + lead.phone} className="p-2 bg-green-500/20 rounded-lg hover:bg-green-500/30 transition" title="Call">📞</a>}
|
||
{lead.email && <a href={"mailto:" + lead.email} className="p-2 bg-blue-500/20 rounded-lg hover:bg-blue-500/30 transition" title="Email">✉️</a>}
|
||
{lead.website && <a href={lead.website.startsWith("http") ? lead.website : "https://" + lead.website} target="_blank" rel="noopener noreferrer" className="p-2 bg-purple-500/20 rounded-lg hover:bg-purple-500/30 transition" title="Website">🌐</a>}
|
||
<button onClick={() => setSelectedLead(lead)} className="p-2 bg-white/10 rounded-lg hover:bg-white/20 transition" title="Details">👁️</button>
|
||
</div>
|
||
</td>
|
||
</motion.tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Email Outreach Tab */}
|
||
{tab === "email" && (
|
||
<div className="space-y-6">
|
||
<div className="bg-blue-500/10 border border-blue-500/30 rounded-xl p-6">
|
||
<h3 className="text-lg font-bold mb-2">📧 Email Outreach</h3>
|
||
<p className="text-white/60 mb-4">Leads with email addresses. Click "Send" to open your email client with pre-filled template.</p>
|
||
<div className="grid gap-4">
|
||
{leads.filter(l => l.email).map(lead => (
|
||
<div key={lead.id} className="bg-white/5 rounded-lg p-4 flex items-center justify-between">
|
||
<div>
|
||
<p className="font-bold">{lead.name}</p>
|
||
<p className="text-sm text-white/60">{lead.email}</p>
|
||
<p className="text-xs text-white/40">{categoryIcons[lead.category]} {lead.category}</p>
|
||
</div>
|
||
<div className="flex gap-2">
|
||
<button onClick={() => updateStatus(lead.id, "contacted")} className="px-3 py-1 bg-yellow-500/20 text-yellow-400 rounded text-sm">Mark Emailed</button>
|
||
<a href={`mailto:${lead.email}?subject=Tu empleado IA 24/7 - SiteMente&body=Hola,%0D%0A%0D%0AVi tu negocio y me gustaría ofrecerte una solución que puede revolucionar tu atención al cliente.%0D%0A%0D%0ACon SiteMente tienes un empleado IA disponible 24/7 que:%0D%0A- Responde preguntas de clientes%0D%0A- Gestiona reservas automáticamente%0D%0A- Habla en español, inglés, francés, alemán...%0D%0A%0D%0A¿Te interesa ver una demo? Es gratis y sin compromiso.%0D%0A%0D%0ASaludos`} className="px-4 py-2 bg-blue-500 hover:bg-blue-600 rounded-lg font-medium">✉️ Send Email</a>
|
||
</div>
|
||
</div>
|
||
))}
|
||
{leads.filter(l => l.email).length === 0 && (
|
||
<p className="text-white/40 text-center py-8">No leads with email found. Research more leads!</p>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="bg-green-500/10 border border-green-500/30 rounded-xl p-6">
|
||
<h3 className="text-lg font-bold mb-2">🤖 AI Calling (Coming Soon)</h3>
|
||
<p className="text-white/60">Automated voice calls to leads - requires your confirmation before each call.</p>
|
||
<p className="text-sm text-white/40 mt-2">Integrating with Vapi - stay tuned!</p>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</main>
|
||
|
||
{selectedLead && (
|
||
<div className="fixed inset-0 bg-black/70 flex items-center justify-center z-50 p-4">
|
||
<div className="bg-[#1a1625] border border-white/20 rounded-2xl p-6 max-w-lg w-full max-h-[90vh] overflow-y-auto">
|
||
<div className="flex items-start justify-between mb-4">
|
||
<div>
|
||
<h3 className="text-xl font-bold">{selectedLead.name}</h3>
|
||
<p className="text-white/60">{categoryIcons[selectedLead.category]} {selectedLead.category}</p>
|
||
</div>
|
||
<button onClick={() => setSelectedLead(null)} className="text-white/40 hover:text-white">✕</button>
|
||
</div>
|
||
<div className="space-y-4">
|
||
<div><label className="text-sm text-white/60">Address</label><p>{selectedLead.address}</p></div>
|
||
<div className="flex gap-4">
|
||
<div className="flex-1"><label className="text-sm text-white/60">Phone</label><p className={selectedLead.phone ? "text-green-400" : "text-red-400"}>{selectedLead.phone || "—"}</p></div>
|
||
<div className="flex-1"><label className="text-sm text-white/60">Website</label><p className={selectedLead.website ? "" : "text-red-400"}>{selectedLead.website || "—"}</p></div>
|
||
</div>
|
||
<div><label className="text-sm text-white/60">Score</label><p className="text-2xl font-bold text-brand-pink">{selectedLead.score}/10</p></div>
|
||
<div><label className="text-sm text-white/60">Notes</label><p>{selectedLead.notes}</p></div>
|
||
<div><label className="text-sm text-white/60">Next Action</label><p className="text-green-400">{selectedLead.nextAction}</p></div>
|
||
</div>
|
||
<div className="flex gap-2 mt-6">
|
||
{selectedLead.phone && <a href={"tel:" + selectedLead.phone} className="flex-1 py-2 bg-green-500 rounded-lg text-center font-medium">📞 Call</a>}
|
||
{selectedLead.email && <a href={"mailto:" + selectedLead.email} className="flex-1 py-2 bg-blue-500 rounded-lg text-center font-medium">✉️ Email</a>}
|
||
<button onClick={() => { updateStatus(selectedLead.id, "won"); setSelectedLead(null); }} className="flex-1 py-2 bg-green-600 rounded-lg font-medium">✅ Won</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|