diff --git a/apps/api/src/routers/lead.ts b/apps/api/src/routers/lead.ts
index b063de5..2799722 100644
--- a/apps/api/src/routers/lead.ts
+++ b/apps/api/src/routers/lead.ts
@@ -1,10 +1,11 @@
import { TRPCError } from '@trpc/server';
-import { asc, desc, eq, sql } from 'drizzle-orm';
+import { and, asc, desc, eq, inArray, isNotNull, lte, sql } from 'drizzle-orm';
import { lead, leadContact, leadTouchpoint, deal, area, user, project } from '@cockpit/db';
import type { Db } from '@cockpit/db';
import { LeadSchemas } from '@cockpit/shared';
import { router, protectedProcedure } from '../trpc.js';
import { parseCsv, normalizeHeader } from '../lib/csv.js';
+import { todayIso } from '../lib/werktag.js';
const SALES_AREA_SLUG = 'vertrieb';
@@ -30,6 +31,35 @@ export const leadRouter = router({
return rows.map((r) => ({ ...r.lead, ownerName: r.ownerName, projectName: r.projectName }));
}),
+ /**
+ * Fällige Wiedervorlagen: aktive Leads (cold/warm/hot), deren geplanter nächster
+ * Schritt heute oder überfällig ist. naechsterSchrittDatum wurde erfasst, aber
+ * nie als Liste gebündelt — so geht keine warme Spur mehr unter.
+ */
+ dueFollowUps: protectedProcedure.query(async ({ ctx }) => {
+ const today = todayIso();
+ const rows = await ctx.db
+ .select({
+ id: lead.id,
+ firma: lead.firma,
+ status: lead.status,
+ ownerName: user.name,
+ naechsterSchritt: lead.naechsterSchritt,
+ faelligAm: lead.naechsterSchrittDatum,
+ })
+ .from(lead)
+ .leftJoin(user, eq(user.id, lead.ownerUserId))
+ .where(
+ and(
+ inArray(lead.status, ['cold', 'warm', 'hot']),
+ isNotNull(lead.naechsterSchrittDatum),
+ lte(lead.naechsterSchrittDatum, today),
+ ),
+ )
+ .orderBy(asc(lead.naechsterSchrittDatum));
+ return rows;
+ }),
+
// Leads, die einem Projekt zugeordnet sind (Vertriebs-Sicht im Projekt).
forProject: protectedProcedure
.input(LeadSchemas.leadsForProjectInput)
diff --git a/apps/web/src/routes/leads.tsx b/apps/web/src/routes/leads.tsx
index bf07ffc..289dae4 100644
--- a/apps/web/src/routes/leads.tsx
+++ b/apps/web/src/routes/leads.tsx
@@ -1,5 +1,6 @@
import { useState } from 'react';
import { Link } from 'react-router';
+import { BellRing } from 'lucide-react';
import { clsx } from 'clsx';
import { toast } from 'sonner';
import { trpc } from '../lib/trpc';
@@ -41,6 +42,8 @@ export function LeadsRoute() {
+
+
{leads.length === 0 ? (
@@ -75,6 +78,36 @@ export function LeadsRoute() {
);
}
+function DueFollowUps() {
+ const q = trpc.lead.dueFollowUps.useQuery();
+ if (!q.data || q.data.length === 0) return null;
+ return (
+
+
+
+
+ Fällige Wiedervorlagen ({q.data.length})
+
+
+ {q.data.map((l) => (
+ -
+
+ {l.firma}
+ {l.naechsterSchritt && (
+ — {l.naechsterSchritt}
+ )}
+
+
+ {l.faelligAm}
+
+
+ ))}
+
+
+
+ );
+}
+
function LeadImportDialog({ onClose }: { onClose: () => void }) {
const projectsQ = trpc.project.list.useQuery();
const projects = (projectsQ.data ?? []).map((r) => ({ id: r.project.id, name: r.project.name }));