mein-solar: full feature set

- Schema: companyType, topics, salesScore, salesReason, offerPackage, approved, approvedAt, SearchHistory table
- /api/search-history: GET (by mode) + POST (save query)
- /api/ai-search: stadtwerke/industrie/custom prompts with history dedup
- /api/enrich-leads: website scraping + GPT-4o-mini enrichment (fire-and-forget after each job)
- /api/generate-email: personalized outreach via GPT-4o
- Suche page: 3 mode tabs (Stadtwerke/Industrie/Freie Suche), Alle-Bundesländer queue button, AiSearchModal gets searchMode + history
- Leadspeicher: Bewertung dots column, Paket badge column, Freigeben toggle button, email generator in SidePanel, approved-only export option
- Leads API: approvedOnly + companyType filters, new fields in PATCH

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Timo Uttenweiler
2026-04-08 21:06:07 +02:00
parent e5172cbdc5
commit 54e0d22f9c
14 changed files with 866 additions and 47 deletions

View File

@@ -11,9 +11,10 @@ interface Query {
interface AiSearchModalProps {
onStart: (queries: Query[]) => void;
onClose: () => void;
searchMode?: string;
}
export function AiSearchModal({ onStart, onClose }: AiSearchModalProps) {
export function AiSearchModal({ onStart, onClose, searchMode = "custom" }: AiSearchModalProps) {
const [description, setDescription] = useState("");
const [loading, setLoading] = useState(false);
const [queries, setQueries] = useState<Query[]>([]);
@@ -26,10 +27,19 @@ export function AiSearchModal({ onStart, onClose }: AiSearchModalProps) {
setError("");
setQueries([]);
try {
// Load history for this mode
let history: Array<{ query: string; region: string }> = [];
if (searchMode !== "custom") {
try {
const hRes = await fetch(`/api/search-history?mode=${searchMode}`);
if (hRes.ok) history = await hRes.json() as Array<{ query: string; region: string }>;
} catch { /* ignore */ }
}
const res = await fetch("/api/ai-search", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ description }),
body: JSON.stringify({ description, searchMode, history }),
});
const data = await res.json() as { queries?: Query[]; error?: string };
if (!res.ok || !data.queries) throw new Error(data.error || "Fehler");