Stitch redesign, Energieversorger-Kampagne, UI improvements
- Apply Stitch design system to leadspeicher, suche, TopBar, globals.css - Add Energieversorger queue campaign (Netzbetreiber, Fernwärme, Industriepark) with BW + Bayern priority, tracks usage per term+location combo - Remove TopBar right-side actions (Leads finden, bell, settings) - Remove mode tabs from manual search, rename KI button - Fix Google Fonts @import order (move to <link> in layout.tsx) - Add cursor-pointer globally via globals.css - Responsive fixes for campaign buttons and KI button - Fix .dockerignore to exclude .env from image build - Add stadtwerke-cities API + city data (50 cities per Bundesland) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -153,14 +153,14 @@ function SidePanel({ lead, onClose, onUpdate, onDelete }: {
|
||||
<div className="fixed inset-0 z-40 flex justify-end" onClick={onClose}>
|
||||
<div className="absolute inset-0 bg-black/40 backdrop-blur-sm" />
|
||||
<div
|
||||
className="relative z-50 w-full max-w-sm h-full bg-[#0e0e1a] border-l border-[#1e1e2e] flex flex-col overflow-hidden"
|
||||
style={{ animation: "slideIn 200ms ease-out" }}
|
||||
className="relative z-50 w-full max-w-sm h-full flex flex-col overflow-hidden"
|
||||
style={{ background: "#161c21", borderLeft: "1px solid rgba(255,255,255,0.07)", animation: "slideIn 200ms ease-out" }}
|
||||
onClick={e => e.stopPropagation()}
|
||||
>
|
||||
<style>{`@keyframes slideIn { from { transform: translateX(100%) } to { transform: translateX(0) } }`}</style>
|
||||
|
||||
{/* Header */}
|
||||
<div className="border-b border-[#1e1e2e] p-4 flex items-start gap-3 flex-shrink-0">
|
||||
<div className="p-4 flex items-start gap-3 flex-shrink-0" style={{ borderBottom: "1px solid rgba(255,255,255,0.07)" }}>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h2 className="text-base font-bold text-white truncate">{lead.companyName || lead.domain || "Unbekannt"}</h2>
|
||||
{lead.domain && (
|
||||
@@ -169,7 +169,7 @@ function SidePanel({ lead, onClose, onUpdate, onDelete }: {
|
||||
<Globe className="w-3 h-3" />{lead.domain}
|
||||
</a>
|
||||
)}
|
||||
{lead.industry && <p className="text-xs text-purple-400 mt-0.5">{lead.industry}</p>}
|
||||
{lead.industry && <p className="text-xs mt-0.5" style={{ color: "#adc7ff" }}>{lead.industry}</p>}
|
||||
</div>
|
||||
<button onClick={onClose} className="text-gray-500 hover:text-white p-1 rounded flex-shrink-0">
|
||||
<X className="w-4 h-4" />
|
||||
@@ -353,7 +353,7 @@ function SidePanel({ lead, onClose, onUpdate, onDelete }: {
|
||||
</div>
|
||||
|
||||
{/* Delete */}
|
||||
<div className="p-4 border-t border-[#1e1e2e] flex-shrink-0">
|
||||
<div className="p-4 flex-shrink-0" style={{ borderTop: "1px solid rgba(255,255,255,0.07)" }}>
|
||||
<button
|
||||
onClick={() => { onDelete(lead.id); onClose(); }}
|
||||
className="w-full flex items-center justify-center gap-2 px-3 py-2 rounded-lg bg-red-500/10 text-red-400 border border-red-500/20 hover:bg-red-500/20 text-sm transition-all"
|
||||
@@ -551,7 +551,7 @@ export default function LeadVaultPage() {
|
||||
|
||||
function SortIcon({ field }: { field: string }) {
|
||||
if (sortBy !== field) return <ArrowUpDown className="w-3 h-3 text-gray-600" />;
|
||||
return sortDir === "asc" ? <ArrowUp className="w-3 h-3 text-purple-400" /> : <ArrowDown className="w-3 h-3 text-purple-400" />;
|
||||
return sortDir === "asc" ? <ArrowUp className="w-3 h-3" style={{ color: "#adc7ff" }} /> : <ArrowDown className="w-3 h-3" style={{ color: "#adc7ff" }} />;
|
||||
}
|
||||
|
||||
const allSelected = leads.length > 0 && leads.every(l => selected.has(l.id));
|
||||
@@ -559,21 +559,22 @@ export default function LeadVaultPage() {
|
||||
return (
|
||||
<div className="space-y-5 max-w-full px-[72px] py-[72px]">
|
||||
{/* Header */}
|
||||
<div className="relative rounded-2xl bg-gradient-to-r from-purple-600/10 to-blue-600/10 border border-[#1e1e2e] p-6 overflow-hidden">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-purple-500/5 to-transparent" />
|
||||
<div className="relative rounded-2xl p-6 overflow-hidden" style={{ background: "#161c21", border: "1px solid rgba(255,255,255,0.05)" }}>
|
||||
<div className="absolute -top-24 -right-24 w-64 h-64 rounded-full pointer-events-none" style={{ background: "rgba(173,199,255,0.08)", filter: "blur(80px)" }} />
|
||||
<div className="relative flex items-center justify-between">
|
||||
<div>
|
||||
<div className="flex items-center gap-2 text-sm text-purple-400 mb-2">
|
||||
<div className="flex items-center gap-2 text-sm mb-2" style={{ color: "#adc7ff" }}>
|
||||
<Database className="w-4 h-4" />
|
||||
<span>Zentrale Datenbank</span>
|
||||
<span style={{ fontFamily: "Inter, sans-serif", fontWeight: 500, letterSpacing: "0.05em", textTransform: "uppercase", fontSize: 11 }}>Zentrale Datenbank</span>
|
||||
</div>
|
||||
<h1 className="text-2xl font-bold text-white">🗄️ Leadspeicher</h1>
|
||||
<p className="text-gray-400 mt-1 text-sm">Alle Leads an einem Ort.</p>
|
||||
<h1 className="text-2xl font-extrabold" style={{ fontFamily: "Manrope, sans-serif", color: "#dce3ea" }}>Leadspeicher</h1>
|
||||
<p className="mt-1 text-sm" style={{ color: "#8b909f" }}>Alle Leads an einem Ort.</p>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={() => setExportOpen(v => !v)}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg border border-purple-500/30 bg-purple-500/10 text-purple-300 hover:bg-purple-500/20 text-sm transition-all"
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm transition-all"
|
||||
style={{ border: "1px solid rgba(173,199,255,0.2)", background: "rgba(173,199,255,0.08)", color: "#adc7ff" }}
|
||||
>
|
||||
<Download className="w-3.5 h-3.5" /> Export
|
||||
</button>
|
||||
@@ -585,7 +586,7 @@ export default function LeadVaultPage() {
|
||||
{exportOpen && (
|
||||
<>
|
||||
<div className="fixed inset-0 z-40" onClick={() => setExportOpen(false)} />
|
||||
<div className="fixed top-20 right-6 z-50 bg-[#1a1a28] border border-[#2e2e3e] rounded-lg shadow-2xl p-1 min-w-[230px]">
|
||||
<div className="fixed top-20 right-6 z-50 rounded-lg shadow-2xl p-1 min-w-[230px]" style={{ background: "#1a2025", border: "1px solid rgba(255,255,255,0.08)" }}>
|
||||
{([
|
||||
["Aktuelle Ansicht", () => exportFile("xlsx")],
|
||||
["Nur mit E-Mail", () => exportFile("xlsx", true)],
|
||||
@@ -604,33 +605,36 @@ export default function LeadVaultPage() {
|
||||
{stats && (
|
||||
<div className="grid grid-cols-4 gap-4">
|
||||
{[
|
||||
{ label: "Leads gesamt", value: stats.total, color: "#a78bfa" },
|
||||
{ label: "Neu / Nicht kontaktiert", value: stats.new, color: "#60a5fa" },
|
||||
{ label: "Kontaktiert / In Bearbeitung", value: stats.contacted, color: "#2dd4bf" },
|
||||
{ label: "Mit verifizierter E-Mail", value: stats.withEmail, color: "#34d399" },
|
||||
].map(({ label, value, color }) => (
|
||||
<Card key={label} className="bg-[#111118] border-[#1e1e2e] p-4">
|
||||
<p className="text-xs text-gray-500 mb-1">{label}</p>
|
||||
{ label: "Leads gesamt", value: stats.total, color: "#adc7ff", accent: "rgba(173,199,255,0.15)" },
|
||||
{ label: "Neu / Unbearbeitet", value: stats.new, color: "#adc7ff", accent: "rgba(173,199,255,0.1)" },
|
||||
{ label: "Kontaktiert", value: stats.contacted, color: "#a0d82c", accent: "rgba(160,216,44,0.1)" },
|
||||
{ label: "Mit E-Mail", value: stats.withEmail, color: "#ffb77b", accent: "rgba(255,183,123,0.1)" },
|
||||
].map(({ label, value, color, accent }) => (
|
||||
<div key={label} className="rounded-xl p-4" style={{ background: "#161c21", border: "1px solid rgba(255,255,255,0.05)" }}>
|
||||
<p className="text-xs font-medium uppercase tracking-wider mb-3" style={{ color: "#8b909f", fontFamily: "Inter, sans-serif", letterSpacing: "0.08em" }}>{label}</p>
|
||||
<div className="flex items-end justify-between">
|
||||
<p className="text-2xl font-bold text-white">{value.toLocaleString()}</p>
|
||||
<Sparkline data={stats.dailyCounts.map(d => d.count)} color={color} />
|
||||
<p className="text-3xl font-extrabold" style={{ fontFamily: "Manrope, sans-serif", color: "#dce3ea" }}>{value.toLocaleString()}</p>
|
||||
<div className="rounded-lg p-2" style={{ background: accent }}>
|
||||
<Sparkline data={stats.dailyCounts.map(d => d.count)} color={color} />
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Quick SERP */}
|
||||
<Card className="bg-[#111118] border-[#1e1e2e] overflow-hidden">
|
||||
<div className="rounded-xl overflow-hidden" style={{ background: "#161c21", border: "1px solid rgba(255,255,255,0.05)" }}>
|
||||
<button
|
||||
onClick={() => setSerpOpen(!serpOpen)}
|
||||
className="w-full flex items-center justify-between px-5 py-3.5 text-sm font-medium text-gray-300 hover:text-white transition-colors"
|
||||
className="w-full flex items-center justify-between px-5 py-3.5 text-sm font-medium transition-colors"
|
||||
style={{ color: "#c1c6d6", fontFamily: "Inter, sans-serif" }}
|
||||
>
|
||||
<span className="flex items-center gap-2"><Search className="w-4 h-4 text-purple-400" />⚡ Schnell neue Suche</span>
|
||||
{serpOpen ? <ChevronUp className="w-4 h-4" /> : <ChevronDown className="w-4 h-4" />}
|
||||
<span className="flex items-center gap-2" style={{ color: "#adc7ff" }}><Search className="w-4 h-4" />⚡ Schnell neue Suche</span>
|
||||
{serpOpen ? <ChevronUp className="w-4 h-4" style={{ color: "#8b909f" }} /> : <ChevronDown className="w-4 h-4" style={{ color: "#8b909f" }} />}
|
||||
</button>
|
||||
{serpOpen && (
|
||||
<div className="px-5 pb-5 border-t border-[#1e1e2e] pt-4 space-y-4">
|
||||
<div className="px-5 pb-5 pt-4 space-y-4" style={{ borderTop: "1px solid rgba(255,255,255,0.05)" }}>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="col-span-2">
|
||||
<Input
|
||||
@@ -638,14 +642,15 @@ export default function LeadVaultPage() {
|
||||
value={serpQuery}
|
||||
onChange={e => setSerpQuery(e.target.value)}
|
||||
onKeyDown={e => e.key === "Enter" && runQuickSerp()}
|
||||
className="bg-[#0d0d18] border-[#2e2e3e] text-white placeholder:text-gray-600 focus:border-purple-500"
|
||||
className="text-white placeholder:text-gray-600"
|
||||
style={{ background: "#080f13", borderColor: "rgba(255,255,255,0.08)" }}
|
||||
/>
|
||||
</div>
|
||||
<Select value={serpCount} onValueChange={v => setSerpCount(v ?? "25")}>
|
||||
<SelectTrigger className="bg-[#0d0d18] border-[#2e2e3e] text-white">
|
||||
<SelectTrigger className="text-white" style={{ background: "#080f13", borderColor: "rgba(255,255,255,0.08)" }}>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-[#111118] border-[#2e2e3e]">
|
||||
<SelectContent style={{ background: "#1a2025", borderColor: "rgba(255,255,255,0.08)" }}>
|
||||
{["10", "25", "50", "100"].map(v => (
|
||||
<SelectItem key={v} value={v} className="text-gray-300">{v} Ergebnisse</SelectItem>
|
||||
))}
|
||||
@@ -653,41 +658,46 @@ export default function LeadVaultPage() {
|
||||
</Select>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<label className="flex items-center gap-2 text-sm text-gray-400 cursor-pointer">
|
||||
<label className="flex items-center gap-2 text-sm cursor-pointer" style={{ color: "#8b909f" }}>
|
||||
<input type="checkbox" checked={serpFilter} onChange={e => setSerpFilter(e.target.checked)} className="rounded" />
|
||||
Social-Media / Verzeichnisse herausfiltern
|
||||
</label>
|
||||
<Button
|
||||
<button
|
||||
onClick={runQuickSerp}
|
||||
disabled={serpRunning || !serpQuery.trim()}
|
||||
className="bg-gradient-to-r from-purple-500 to-blue-600 hover:from-purple-600 hover:to-blue-700 text-white font-medium px-6"
|
||||
className="px-6 py-2 rounded-xl font-bold text-sm transition-all active:scale-95 disabled:opacity-50"
|
||||
style={{ fontFamily: "Manrope, sans-serif", background: "linear-gradient(135deg, #adc7ff, #1a73e8)", color: "#002e68" }}
|
||||
>
|
||||
{serpRunning ? "Suche läuft..." : "🔍 SERP Capture starten"}
|
||||
</Button>
|
||||
{serpRunning ? "Suche läuft..." : "SERP Capture starten"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Filter Bar */}
|
||||
<Card className="bg-[#111118] border-[#1e1e2e] p-4 space-y-3">
|
||||
<div className="rounded-xl p-4 space-y-3" style={{ background: "#161c21", border: "1px solid rgba(255,255,255,0.05)" }}>
|
||||
<div className="flex items-center gap-3 flex-wrap">
|
||||
{/* Search */}
|
||||
<div className="relative flex-1 min-w-[200px]">
|
||||
<Search className="absolute left-2.5 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-gray-500" />
|
||||
<Search className="absolute left-2.5 top-1/2 -translate-y-1/2 w-3.5 h-3.5" style={{ color: "#8b909f" }} />
|
||||
<input
|
||||
value={search}
|
||||
onChange={e => setSearch(e.target.value)}
|
||||
placeholder="Domain, Firma, Name, E-Mail suchen..."
|
||||
className="w-full bg-[#0d0d18] border border-[#2e2e3e] rounded-lg pl-8 pr-3 py-1.5 text-sm text-white outline-none focus:border-purple-500"
|
||||
className="w-full rounded-lg pl-8 pr-3 py-1.5 text-sm text-white outline-none"
|
||||
style={{ background: "#080f13", border: "1px solid rgba(255,255,255,0.08)" }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Has Email toggle */}
|
||||
<div className="flex gap-1 bg-[#0d0d18] border border-[#2e2e3e] rounded-lg p-1">
|
||||
<div className="flex gap-1 rounded-lg p-1" style={{ background: "#080f13", border: "1px solid rgba(255,255,255,0.08)" }}>
|
||||
{[["", "Alle"], ["yes", "Mit E-Mail"], ["no", "Ohne E-Mail"]].map(([v, l]) => (
|
||||
<button key={v} onClick={() => { setFilterHasEmail(v); setPage(1); }}
|
||||
className={`px-2.5 py-1 rounded text-xs font-medium transition-all ${filterHasEmail === v ? "bg-purple-500/30 text-purple-300" : "text-gray-500 hover:text-gray-300"}`}>
|
||||
className="px-2.5 py-1 rounded text-xs font-medium transition-all"
|
||||
style={filterHasEmail === v
|
||||
? { background: "rgba(173,199,255,0.15)", color: "#adc7ff" }
|
||||
: { color: "#8b909f" }}>
|
||||
{l}
|
||||
</button>
|
||||
))}
|
||||
@@ -696,11 +706,10 @@ export default function LeadVaultPage() {
|
||||
{/* Kontaktiert toggle */}
|
||||
<button
|
||||
onClick={() => { setFilterContacted(v => !v); setPage(1); }}
|
||||
className={`px-2.5 py-1.5 rounded-lg text-xs font-medium border transition-all ${
|
||||
filterContacted
|
||||
? "bg-teal-500/20 text-teal-300 border-teal-500/30"
|
||||
: "bg-[#0d0d18] text-gray-500 border-[#2e2e3e] hover:text-gray-300 hover:border-[#4e4e6e]"
|
||||
}`}
|
||||
className="px-2.5 py-1.5 rounded-lg text-xs font-medium transition-all"
|
||||
style={filterContacted
|
||||
? { background: "rgba(160,216,44,0.15)", color: "#a0d82c", border: "1px solid rgba(160,216,44,0.3)" }
|
||||
: { background: "#080f13", color: "#8b909f", border: "1px solid rgba(255,255,255,0.08)" }}
|
||||
>
|
||||
Kontaktiert
|
||||
</button>
|
||||
@@ -708,11 +717,10 @@ export default function LeadVaultPage() {
|
||||
{/* Favoriten toggle */}
|
||||
<button
|
||||
onClick={() => { setFilterFavorite(v => !v); setPage(1); }}
|
||||
className={`px-2.5 py-1.5 rounded-lg text-xs font-medium border transition-all ${
|
||||
filterFavorite
|
||||
? "bg-amber-500/20 text-amber-300 border-amber-500/30"
|
||||
: "bg-[#0d0d18] text-gray-500 border-[#2e2e3e] hover:text-gray-300 hover:border-[#4e4e6e]"
|
||||
}`}
|
||||
className="px-2.5 py-1.5 rounded-lg text-xs font-medium transition-all"
|
||||
style={filterFavorite
|
||||
? { background: "rgba(255,183,123,0.15)", color: "#ffb77b", border: "1px solid rgba(255,183,123,0.3)" }
|
||||
: { background: "#080f13", color: "#8b909f", border: "1px solid rgba(255,255,255,0.08)" }}
|
||||
>
|
||||
★ Favoriten
|
||||
</button>
|
||||
@@ -720,55 +728,58 @@ export default function LeadVaultPage() {
|
||||
{/* Clear + count */}
|
||||
<div className="flex items-center gap-2 ml-auto">
|
||||
{(search || filterHasEmail || filterContacted || filterFavorite || filterSearchTerms.length) && (
|
||||
<button onClick={clearFilters} className="text-xs text-gray-500 hover:text-white flex items-center gap-1">
|
||||
<button onClick={clearFilters} className="text-xs flex items-center gap-1" style={{ color: "#8b909f" }}>
|
||||
<X className="w-3 h-3" /> Filter zurücksetzen
|
||||
</button>
|
||||
)}
|
||||
<span className="text-xs text-gray-500">{total.toLocaleString()} Leads</span>
|
||||
<span className="text-xs" style={{ color: "#8b909f" }}>{total.toLocaleString()} Leads</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Bulk Actions */}
|
||||
{selected.size > 0 && (
|
||||
<div className="flex items-center gap-3 bg-purple-500/10 border border-purple-500/20 rounded-xl px-4 py-2.5">
|
||||
<span className="text-sm text-purple-300 font-medium">{selected.size} ausgewählt</span>
|
||||
<div className="flex items-center gap-3 rounded-xl px-4 py-2.5" style={{ background: "rgba(173,199,255,0.07)", border: "1px solid rgba(173,199,255,0.15)" }}>
|
||||
<span className="text-sm font-medium" style={{ color: "#adc7ff", fontFamily: "Manrope, sans-serif" }}>{selected.size} ausgewählt</span>
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
<div className="flex gap-1">
|
||||
<input
|
||||
value={bulkTag}
|
||||
onChange={e => setBulkTag(e.target.value)}
|
||||
placeholder="Tag hinzufügen..."
|
||||
className="bg-[#1a1a28] border border-[#2e2e3e] text-gray-300 text-xs rounded px-2 py-1 w-32 outline-none"
|
||||
className="text-gray-300 text-xs rounded px-2 py-1 w-32 outline-none"
|
||||
style={{ background: "#1a2025", border: "1px solid rgba(255,255,255,0.08)" }}
|
||||
/>
|
||||
<button onClick={() => { if (bulkTag) { bulkAction("tag", bulkTag); setBulkTag(""); } }}
|
||||
className="text-xs px-2 py-1 rounded bg-[#2e2e3e] text-gray-300 hover:bg-[#3e3e5e]">
|
||||
className="text-xs px-2 py-1 rounded text-gray-300"
|
||||
style={{ background: "#242b30" }}>
|
||||
<Tag className="w-3 h-3" />
|
||||
</button>
|
||||
</div>
|
||||
<button onClick={() => bulkAction("delete", "")}
|
||||
className="flex items-center gap-1 text-xs px-2.5 py-1 rounded bg-red-500/20 text-red-300 border border-red-500/30 hover:bg-red-500/30">
|
||||
className="flex items-center gap-1 text-xs px-2.5 py-1 rounded text-red-300 border border-red-500/30"
|
||||
style={{ background: "rgba(255,100,100,0.1)" }}>
|
||||
<Trash2 className="w-3 h-3" /> Löschen
|
||||
</button>
|
||||
<button onClick={() => setSelected(new Set())} className="text-xs text-gray-500 hover:text-white px-2">✕</button>
|
||||
<button onClick={() => setSelected(new Set())} className="text-xs px-2" style={{ color: "#8b909f" }}>✕</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Table */}
|
||||
<Card className="bg-[#111118] border-[#1e1e2e] overflow-hidden">
|
||||
<div className="rounded-xl overflow-hidden" style={{ background: "#161c21", border: "1px solid rgba(255,255,255,0.05)" }}>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-[#1e1e2e]">
|
||||
<tr style={{ borderBottom: "1px solid rgba(255,255,255,0.05)" }}>
|
||||
<th className="px-3 py-2.5 text-left">
|
||||
<button onClick={() => {
|
||||
if (allSelected) setSelected(new Set());
|
||||
else setSelected(new Set(leads.map(l => l.id)));
|
||||
}}>
|
||||
{allSelected
|
||||
? <CheckSquare className="w-4 h-4 text-purple-400" />
|
||||
? <CheckSquare className="w-4 h-4" style={{ color: "#adc7ff" }} />
|
||||
: <Square className="w-4 h-4 text-gray-600" />}
|
||||
</button>
|
||||
</th>
|
||||
@@ -805,7 +816,7 @@ export default function LeadVaultPage() {
|
||||
: "Noch keine Leads. Pipeline ausführen oder Quick SERP nutzen."}
|
||||
</p>
|
||||
{search && (
|
||||
<button onClick={clearFilters} className="mt-2 text-xs text-purple-400 hover:underline">Filter zurücksetzen</button>
|
||||
<button onClick={clearFilters} className="mt-2 text-xs hover:underline" style={{ color: "#adc7ff" }}>Filter zurücksetzen</button>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
@@ -821,9 +832,13 @@ export default function LeadVaultPage() {
|
||||
<tr
|
||||
key={lead.id}
|
||||
onClick={() => setPanelLead(lead)}
|
||||
className={`border-b border-[#1a1a28] cursor-pointer transition-colors hover:bg-[#1a1a28] ${
|
||||
isSelected ? "bg-[#1a1a2e]" : i % 2 === 0 ? "bg-[#111118]" : "bg-[#0f0f16]"
|
||||
}`}
|
||||
className="cursor-pointer transition-colors"
|
||||
style={{
|
||||
borderBottom: "1px solid rgba(255,255,255,0.03)",
|
||||
background: isSelected ? "rgba(173,199,255,0.07)" : i % 2 === 0 ? "#161c21" : "#1a2025",
|
||||
}}
|
||||
onMouseEnter={e => { if (!isSelected) e.currentTarget.style.background = "#242b30"; }}
|
||||
onMouseLeave={e => { if (!isSelected) e.currentTarget.style.background = i % 2 === 0 ? "#161c21" : "#1a2025"; }}
|
||||
>
|
||||
<td className="px-3 py-2.5" onClick={e => e.stopPropagation()}>
|
||||
<button onClick={() => {
|
||||
@@ -834,7 +849,7 @@ export default function LeadVaultPage() {
|
||||
});
|
||||
}}>
|
||||
{isSelected
|
||||
? <CheckSquare className="w-4 h-4 text-purple-400" />
|
||||
? <CheckSquare className="w-4 h-4" style={{ color: "#adc7ff" }} />
|
||||
: <Square className="w-4 h-4 text-gray-600" />}
|
||||
</button>
|
||||
</td>
|
||||
@@ -883,11 +898,10 @@ export default function LeadVaultPage() {
|
||||
{lead.sourceTerm ? (
|
||||
<button
|
||||
onClick={e => { e.stopPropagation(); toggleFilter(filterSearchTerms, setFilterSearchTerms, lead.sourceTerm!); setPage(1); }}
|
||||
className={`text-xs px-2 py-0.5 rounded-full border transition-all truncate max-w-full block ${
|
||||
filterSearchTerms.includes(lead.sourceTerm)
|
||||
? "bg-amber-500/20 text-amber-300 border-amber-500/30"
|
||||
: "bg-[#1a1a28] text-gray-400 border-[#2e2e3e] hover:border-amber-500/30 hover:text-amber-300"
|
||||
}`}
|
||||
className="text-xs px-2 py-0.5 rounded-full border transition-all truncate max-w-full block"
|
||||
style={filterSearchTerms.includes(lead.sourceTerm)
|
||||
? { background: "rgba(255,183,123,0.15)", color: "#ffb77b", borderColor: "rgba(255,183,123,0.3)" }
|
||||
: { background: "#1a2025", color: "#8b909f", borderColor: "rgba(255,255,255,0.08)" }}
|
||||
title={lead.sourceTerm}
|
||||
>
|
||||
🔎 {lead.sourceTerm}
|
||||
@@ -1008,20 +1022,22 @@ export default function LeadVaultPage() {
|
||||
|
||||
{/* Pagination */}
|
||||
{pages > 1 && (
|
||||
<div className="flex items-center justify-between px-4 py-3 border-t border-[#1e1e2e]">
|
||||
<div className="flex items-center justify-between px-4 py-3" style={{ borderTop: "1px solid rgba(255,255,255,0.05)" }}>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-xs text-gray-500">Zeilen pro Seite:</span>
|
||||
<span className="text-xs" style={{ color: "#8b909f" }}>Zeilen pro Seite:</span>
|
||||
<select
|
||||
value={perPage}
|
||||
onChange={e => { setPerPage(Number(e.target.value)); setPage(1); }}
|
||||
className="bg-[#0d0d18] border border-[#2e2e3e] text-gray-300 text-xs rounded px-2 py-1"
|
||||
className="text-gray-300 text-xs rounded px-2 py-1"
|
||||
style={{ background: "#080f13", border: "1px solid rgba(255,255,255,0.08)" }}
|
||||
>
|
||||
{[25, 50, 100].map(n => <option key={n} value={n}>{n}</option>)}
|
||||
</select>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<button disabled={page <= 1} onClick={() => setPage(p => p - 1)}
|
||||
className="px-2.5 py-1 rounded text-xs text-gray-400 border border-[#2e2e3e] hover:border-[#4e4e6e] disabled:opacity-30">
|
||||
className="px-2.5 py-1 rounded text-xs text-gray-400 disabled:opacity-30"
|
||||
style={{ border: "1px solid rgba(255,255,255,0.08)" }}>
|
||||
‹ Zurück
|
||||
</button>
|
||||
{Array.from({ length: Math.min(7, pages) }, (_, i) => {
|
||||
@@ -1033,20 +1049,24 @@ export default function LeadVaultPage() {
|
||||
}
|
||||
return (
|
||||
<button key={p} onClick={() => setPage(p)}
|
||||
className={`w-7 h-7 rounded text-xs ${page === p ? "bg-purple-500/30 text-purple-300 border border-purple-500/30" : "text-gray-500 hover:text-gray-300"}`}>
|
||||
className="w-7 h-7 rounded text-xs"
|
||||
style={page === p
|
||||
? { background: "rgba(173,199,255,0.15)", color: "#adc7ff", border: "1px solid rgba(173,199,255,0.3)" }
|
||||
: { color: "#8b909f" }}>
|
||||
{p}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
<button disabled={page >= pages} onClick={() => setPage(p => p + 1)}
|
||||
className="px-2.5 py-1 rounded text-xs text-gray-400 border border-[#2e2e3e] hover:border-[#4e4e6e] disabled:opacity-30">
|
||||
className="px-2.5 py-1 rounded text-xs text-gray-400 disabled:opacity-30"
|
||||
style={{ border: "1px solid rgba(255,255,255,0.08)" }}>
|
||||
Weiter ›
|
||||
</button>
|
||||
</div>
|
||||
<span className="text-xs text-gray-500">Seite {page} von {pages}</span>
|
||||
<span className="text-xs" style={{ color: "#8b909f" }}>Seite {page} von {pages}</span>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Side Panel */}
|
||||
{panelLead && (
|
||||
|
||||
Reference in New Issue
Block a user