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>
This commit is contained in:
82
components/leadspeicher/StatusPopover.tsx
Normal file
82
components/leadspeicher/StatusPopover.tsx
Normal file
@@ -0,0 +1,82 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef } from "react";
|
||||
import { STATUS_OPTIONS, STATUS_MAP } from "./StatusBadge";
|
||||
|
||||
interface StatusPopoverProps {
|
||||
currentStatus: string;
|
||||
onSelect: (status: string) => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export function StatusPopover({ currentStatus, onSelect, onClose }: StatusPopoverProps) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
function handleClick(e: MouseEvent) {
|
||||
if (ref.current && !ref.current.contains(e.target as Node)) {
|
||||
onClose();
|
||||
}
|
||||
}
|
||||
document.addEventListener("mousedown", handleClick);
|
||||
return () => document.removeEventListener("mousedown", handleClick);
|
||||
}, [onClose]);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
style={{
|
||||
position: "absolute",
|
||||
zIndex: 100,
|
||||
top: "calc(100% + 4px)",
|
||||
left: 0,
|
||||
background: "#111118",
|
||||
border: "1px solid #1e1e2e",
|
||||
borderRadius: 8,
|
||||
padding: 4,
|
||||
minWidth: 160,
|
||||
boxShadow: "0 4px 16px rgba(0,0,0,0.4)",
|
||||
}}
|
||||
>
|
||||
{STATUS_OPTIONS.map((opt) => {
|
||||
const cfg = STATUS_MAP[opt.value];
|
||||
const isActive = opt.value === currentStatus;
|
||||
return (
|
||||
<button
|
||||
key={opt.value}
|
||||
onClick={() => { onSelect(opt.value); onClose(); }}
|
||||
style={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 8,
|
||||
padding: "7px 10px",
|
||||
borderRadius: 6,
|
||||
fontSize: 13,
|
||||
color: isActive ? "#ffffff" : "#9ca3af",
|
||||
background: isActive ? "#1e1e2e" : "transparent",
|
||||
border: "none",
|
||||
cursor: "pointer",
|
||||
textAlign: "left",
|
||||
transition: "background 0.1s",
|
||||
}}
|
||||
onMouseEnter={(e) => { if (!isActive) (e.currentTarget as HTMLButtonElement).style.background = "#0d0d18"; }}
|
||||
onMouseLeave={(e) => { if (!isActive) (e.currentTarget as HTMLButtonElement).style.background = "transparent"; }}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
display: "inline-block",
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: 4,
|
||||
background: cfg.color,
|
||||
flexShrink: 0,
|
||||
}}
|
||||
/>
|
||||
{opt.label}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user