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

@@ -0,0 +1,67 @@
-- CreateTable
CREATE TABLE "SearchHistory" (
"id" TEXT NOT NULL PRIMARY KEY,
"query" TEXT NOT NULL,
"region" TEXT NOT NULL,
"searchMode" TEXT NOT NULL,
"executedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- RedefineTables
PRAGMA defer_foreign_keys=ON;
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_Lead" (
"id" TEXT NOT NULL PRIMARY KEY,
"domain" TEXT,
"companyName" TEXT,
"contactName" TEXT,
"contactTitle" TEXT,
"email" TEXT,
"linkedinUrl" TEXT,
"phone" TEXT,
"address" TEXT,
"sourceTab" TEXT NOT NULL,
"sourceTerm" TEXT,
"sourceJobId" TEXT,
"serpTitle" TEXT,
"serpSnippet" TEXT,
"serpRank" INTEGER,
"serpUrl" TEXT,
"emailConfidence" REAL,
"status" TEXT NOT NULL DEFAULT 'new',
"priority" TEXT NOT NULL DEFAULT 'normal',
"notes" TEXT,
"tags" TEXT,
"country" TEXT,
"headcount" TEXT,
"industry" TEXT,
"description" TEXT,
"companyType" TEXT,
"topics" TEXT,
"salesScore" INTEGER,
"salesReason" TEXT,
"offerPackage" TEXT,
"approved" BOOLEAN NOT NULL DEFAULT false,
"approvedAt" DATETIME,
"capturedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"contactedAt" DATETIME,
"updatedAt" DATETIME NOT NULL
);
INSERT INTO "new_Lead" ("address", "capturedAt", "companyName", "contactName", "contactTitle", "contactedAt", "country", "description", "domain", "email", "emailConfidence", "headcount", "id", "industry", "linkedinUrl", "notes", "phone", "priority", "serpRank", "serpSnippet", "serpTitle", "serpUrl", "sourceJobId", "sourceTab", "sourceTerm", "status", "tags", "updatedAt") SELECT "address", "capturedAt", "companyName", "contactName", "contactTitle", "contactedAt", "country", "description", "domain", "email", "emailConfidence", "headcount", "id", "industry", "linkedinUrl", "notes", "phone", "priority", "serpRank", "serpSnippet", "serpTitle", "serpUrl", "sourceJobId", "sourceTab", "sourceTerm", "status", "tags", "updatedAt" FROM "Lead";
DROP TABLE "Lead";
ALTER TABLE "new_Lead" RENAME TO "Lead";
CREATE INDEX "Lead_domain_idx" ON "Lead"("domain");
CREATE INDEX "Lead_status_idx" ON "Lead"("status");
CREATE INDEX "Lead_sourceTab_idx" ON "Lead"("sourceTab");
CREATE INDEX "Lead_capturedAt_idx" ON "Lead"("capturedAt");
CREATE INDEX "Lead_email_idx" ON "Lead"("email");
CREATE INDEX "Lead_approved_idx" ON "Lead"("approved");
CREATE INDEX "Lead_companyType_idx" ON "Lead"("companyType");
PRAGMA foreign_keys=ON;
PRAGMA defer_foreign_keys=OFF;
-- CreateIndex
CREATE INDEX "SearchHistory_searchMode_idx" ON "SearchHistory"("searchMode");
-- CreateIndex
CREATE INDEX "SearchHistory_executedAt_idx" ON "SearchHistory"("executedAt");

View File

@@ -75,6 +75,14 @@ model Lead {
industry String?
description String?
companyType String?
topics String?
salesScore Int?
salesReason String?
offerPackage String?
approved Boolean @default(false)
approvedAt DateTime?
capturedAt DateTime @default(now())
contactedAt DateTime?
updatedAt DateTime @updatedAt
@@ -86,6 +94,19 @@ model Lead {
@@index([sourceTab])
@@index([capturedAt])
@@index([email])
@@index([approved])
@@index([companyType])
}
model SearchHistory {
id String @id @default(cuid())
query String
region String
searchMode String
executedAt DateTime @default(now())
@@index([searchMode])
@@index([executedAt])
}
model LeadEvent {