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:
Timo Uttenweiler
2026-04-09 10:08:00 +02:00
parent 54e0d22f9c
commit 7db914084e
9 changed files with 868 additions and 356 deletions

View File

@@ -4,34 +4,6 @@ import Link from "next/link";
import { usePathname } from "next/navigation";
import { useEffect, useState } from "react";
function OnyvaLogo() {
return (
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
<div
style={{
width: 24,
height: 24,
borderRadius: 6,
background: "linear-gradient(135deg, #3b82f6, #8b5cf6)",
display: "flex",
alignItems: "center",
justifyContent: "center",
flexShrink: 0,
}}
>
{/* 5-pointed star SVG */}
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
<polygon
points="7,1 8.5,5.5 13,5.5 9.5,8.5 10.8,13 7,10.2 3.2,13 4.5,8.5 1,5.5 5.5,5.5"
fill="white"
/>
</svg>
</div>
<span style={{ fontSize: 15, fontWeight: 500, color: "#ffffff" }}>OnyvaLeads</span>
</div>
);
}
export function Topbar() {
const pathname = usePathname();
const [newLeadsCount, setNewLeadsCount] = useState(0);
@@ -48,90 +20,65 @@ export function Topbar() {
return () => clearInterval(t);
}, []);
const tabs = [
{ href: "/suche", label: "Suche" },
{ href: "/leadspeicher", label: "Leadspeicher" },
];
const isSearch = pathname === "/suche" || pathname.startsWith("/suche/");
const isLeadspeicher = pathname === "/leadspeicher" || pathname.startsWith("/leadspeicher/");
return (
<header
<header className="sticky top-0 w-full z-50 flex justify-between items-center px-8 h-16"
style={{
position: "sticky",
top: 0,
zIndex: 50,
height: 52,
background: "#111118",
borderBottom: "1px solid #1e1e2e",
display: "flex",
alignItems: "center",
paddingLeft: 20,
paddingRight: 20,
gap: 16,
flexShrink: 0,
background: "rgba(13, 20, 25, 0.85)",
backdropFilter: "blur(20px)",
WebkitBackdropFilter: "blur(20px)",
boxShadow: "0 20px 40px rgba(0,0,0,0.4)",
}}
>
{/* Logo */}
<Link href="/suche" style={{ textDecoration: "none" }}>
<OnyvaLogo />
</Link>
{/* Tab switcher */}
<div
style={{
background: "#0a0a0f",
borderRadius: 8,
padding: 3,
display: "flex",
gap: 2,
}}
>
{tabs.map((tab) => {
const isActive = pathname === tab.href || pathname.startsWith(tab.href + "/");
return (
<Link
key={tab.href}
href={tab.href}
style={{
display: "flex",
alignItems: "center",
gap: 6,
padding: "4px 12px",
borderRadius: 6,
fontSize: 13,
fontWeight: isActive ? 500 : 400,
color: isActive ? "#ffffff" : "#9ca3af",
background: isActive ? "#111118" : "transparent",
boxShadow: isActive ? "0 1px 3px rgba(0,0,0,0.2)" : "none",
textDecoration: "none",
transition: "all 0.15s",
whiteSpace: "nowrap",
}}
>
{tab.label}
{tab.href === "/leadspeicher" && newLeadsCount > 0 && (
<span
style={{
background: "#3b82f6",
color: "#ffffff",
fontSize: 10,
fontWeight: 600,
minWidth: 18,
height: 18,
borderRadius: 10,
display: "flex",
alignItems: "center",
justifyContent: "center",
padding: "0 4px",
lineHeight: 1,
}}
>
{newLeadsCount > 99 ? "99+" : newLeadsCount}
</span>
)}
</Link>
);
})}
{/* Left: Logo + Nav */}
<div className="flex items-center gap-8">
<Link href="/suche" style={{ textDecoration: "none" }}>
<span className="text-xl font-black text-white tracking-tighter" style={{ fontFamily: "Manrope, sans-serif" }}>
mein-solar | Lead Finder
</span>
</Link>
<nav className="hidden md:flex gap-6 items-center h-full">
<Link
href="/suche"
className={`font-bold tracking-tight transition-all pb-1 text-sm ${
isSearch
? "text-blue-400 border-b-2 border-blue-500"
: "text-slate-400 hover:text-white"
}`}
style={{ fontFamily: "Manrope, sans-serif", textDecoration: "none" }}
>
Lead-Suche
</Link>
<Link
href="/leadspeicher"
className={`font-bold tracking-tight transition-all pb-1 text-sm flex items-center gap-2 ${
isLeadspeicher
? "text-blue-400 border-b-2 border-blue-500"
: "text-slate-400 hover:text-white"
}`}
style={{ fontFamily: "Manrope, sans-serif", textDecoration: "none" }}
>
Leadspeicher
{newLeadsCount > 0 && (
<span
className="text-white font-bold rounded-full flex items-center justify-center px-1"
style={{
background: "#1a73e8",
fontSize: 10,
minWidth: 18,
height: 18,
lineHeight: 1,
}}
>
{newLeadsCount > 99 ? "99+" : newLeadsCount}
</span>
)}
</Link>
</nav>
</div>
</header>
);
}