feat: LeadVault - zentrale Lead-Datenbank mit CRM-Funktionen
- Prisma-Schema: Lead + LeadEvent Modelle (Migration 20260320) - lib/services/leadVault.ts: sinkLeadsToVault mit Deduplizierung - Auto-Sync: alle 4 Pipelines schreiben Leads in LeadVault - GET /api/leads: Filter, Sortierung, Pagination (Server-side) - PATCH/DELETE /api/leads/[id]: Status, Priorität, Tags, Notizen - POST /api/leads/bulk: Bulk-Aktionen für mehrere Leads - GET /api/leads/stats: Statistiken + 7-Tage-Sparkline - POST /api/leads/quick-serp: SERP-Capture ohne Enrichment - GET /api/leads/export: CSV-Export mit allen Feldern - app/leadvault/page.tsx: vollständige UI mit Stats, Quick SERP, Filter-Leiste, sortierbare Tabelle, Bulk-Aktionen, Side Panel - Sidebar: LeadVault-Eintrag mit Live-Badge (neue Leads) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
64
app/api/leads/bulk/route.ts
Normal file
64
app/api/leads/bulk/route.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { prisma } from "@/lib/db";
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
try {
|
||||
const body = await req.json() as {
|
||||
ids: string[];
|
||||
action: "status" | "priority" | "tag" | "delete";
|
||||
value: string;
|
||||
};
|
||||
const { ids, action, value } = body;
|
||||
|
||||
if (!ids?.length) return NextResponse.json({ error: "No IDs provided" }, { status: 400 });
|
||||
|
||||
if (action === "delete") {
|
||||
const { count } = await prisma.lead.deleteMany({ where: { id: { in: ids } } });
|
||||
return NextResponse.json({ updated: count });
|
||||
}
|
||||
|
||||
if (action === "status") {
|
||||
const { count } = await prisma.lead.updateMany({
|
||||
where: { id: { in: ids } },
|
||||
data: { status: value },
|
||||
});
|
||||
// Create events for status change
|
||||
for (const id of ids) {
|
||||
await prisma.leadEvent.create({
|
||||
data: { leadId: id, event: `Status geändert zu "${value}" (Bulk)` },
|
||||
}).catch(() => {}); // ignore if lead was deleted
|
||||
}
|
||||
return NextResponse.json({ updated: count });
|
||||
}
|
||||
|
||||
if (action === "priority") {
|
||||
const { count } = await prisma.lead.updateMany({
|
||||
where: { id: { in: ids } },
|
||||
data: { priority: value },
|
||||
});
|
||||
return NextResponse.json({ updated: count });
|
||||
}
|
||||
|
||||
if (action === "tag") {
|
||||
// Add tag to each lead's tags JSON array
|
||||
const leads = await prisma.lead.findMany({ where: { id: { in: ids } }, select: { id: true, tags: true } });
|
||||
let count = 0;
|
||||
for (const lead of leads) {
|
||||
const existing: string[] = JSON.parse(lead.tags || "[]");
|
||||
if (!existing.includes(value)) {
|
||||
await prisma.lead.update({
|
||||
where: { id: lead.id },
|
||||
data: { tags: JSON.stringify([...existing, value]) },
|
||||
});
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return NextResponse.json({ updated: count });
|
||||
}
|
||||
|
||||
return NextResponse.json({ error: "Unknown action" }, { status: 400 });
|
||||
} catch (err) {
|
||||
console.error("POST /api/leads/bulk error:", err);
|
||||
return NextResponse.json({ error: "Bulk action failed" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user