Files
lead-scraper/components/leadspeicher/LeadsToolbar.tsx
Timo Uttenweiler 60073b97c9 feat: OnyvaLeads customer UI — Suche + Leadspeicher
- New Topbar: logo, 2-tab pill switcher, live "Neu" badge
- /suche page: SearchCard, LoadingCard, ExamplePills
- /leadspeicher page: full leads table with filters, pagination
- StatusBadge, StatusPopover, LeadSidePanel, BulkActionBar
- POST /api/search: unified search entry point → serp-enrich
- Remove Sidebar + old TopBar from layout
- Title: OnyvaLeads, redirect / → /suche

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 16:48:05 +01:00

137 lines
3.9 KiB
TypeScript

"use client";
import { useRef } from "react";
interface LeadsToolbarProps {
search: string;
status: string;
onSearchChange: (v: string) => void;
onStatusChange: (v: string) => void;
exportParams: Record<string, string>;
}
export function LeadsToolbar({
search,
status,
onSearchChange,
onStatusChange,
exportParams,
}: LeadsToolbarProps) {
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
function handleSearchInput(e: React.ChangeEvent<HTMLInputElement>) {
const val = e.target.value;
if (debounceRef.current) clearTimeout(debounceRef.current);
debounceRef.current = setTimeout(() => onSearchChange(val), 300);
// immediate update of input
e.currentTarget.value = val;
}
function handleExport() {
const params = new URLSearchParams({ format: "xlsx", ...exportParams });
window.location.href = `/api/leads/export?${params.toString()}`;
}
return (
<div
style={{
position: "sticky",
top: 52,
zIndex: 40,
background: "#111118",
borderBottom: "1px solid #1e1e2e",
padding: "14px 20px",
display: "flex",
alignItems: "center",
gap: 12,
}}
>
{/* Search input */}
<div style={{ flex: 1, position: "relative" }}>
<div
style={{
position: "absolute",
left: 10,
top: "50%",
transform: "translateY(-50%)",
pointerEvents: "none",
color: "#6b7280",
}}
>
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
<circle cx="6" cy="6" r="4.5" stroke="currentColor" strokeWidth="1.5" />
<line x1="9.5" y1="9.5" x2="12.5" y2="12.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
</svg>
</div>
<input
type="text"
defaultValue={search}
onChange={handleSearchInput}
placeholder="Unternehmen, E-Mail, Branche…"
style={{
width: "100%",
background: "#0d0d18",
border: "1px solid #1e1e2e",
borderRadius: 8,
padding: "8px 12px 8px 32px",
fontSize: 13,
color: "#ffffff",
outline: "none",
boxSizing: "border-box",
}}
onFocus={(e) => { e.currentTarget.style.borderColor = "#3b82f6"; }}
onBlur={(e) => { e.currentTarget.style.borderColor = "#1e1e2e"; }}
/>
</div>
{/* Status filter */}
<select
value={status}
onChange={(e) => onStatusChange(e.target.value)}
style={{
width: 150,
background: "#0d0d18",
border: "1px solid #1e1e2e",
borderRadius: 8,
padding: "8px 12px",
fontSize: 13,
color: "#ffffff",
cursor: "pointer",
outline: "none",
flexShrink: 0,
}}
>
<option value="">Alle Status</option>
<option value="new">Neu</option>
<option value="contacted">Kontaktiert</option>
<option value="in_progress">In Bearbeitung</option>
<option value="not_relevant">Nicht relevant</option>
<option value="converted">Konvertiert</option>
</select>
{/* Export button */}
<button
onClick={handleExport}
style={{
background: "#0d0d18",
border: "1px solid #2e2e3e",
borderRadius: 8,
padding: "8px 14px",
fontSize: 13,
color: "#9ca3af",
cursor: "pointer",
flexShrink: 0,
display: "flex",
alignItems: "center",
gap: 6,
transition: "color 0.15s",
}}
onMouseEnter={(e) => { (e.currentTarget as HTMLButtonElement).style.color = "#ffffff"; }}
onMouseLeave={(e) => { (e.currentTarget as HTMLButtonElement).style.color = "#9ca3af"; }}
>
Excel Export
</button>
</div>
);
}