feat(10-03): add FollowUpWidget and integrate modals in LeadDetail

- Created FollowUpWidget.tsx displaying leads needing follow-up (last_contact_date > 7 days)
- Widget shows count and lists top 3 leads with quick-access contact links
- Integrated FollowUpWidget into admin dashboard with Suspense fallback
- Updated LeadDetail.tsx to import and render SendQuoteModal alongside LogActivityModal
- Updated dashboard grid to accommodate follow-up widget (5 columns on lg screens)
- Widget follows CRM-06 requirement for follow-up reminders on dashboard

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 12:36:52 +02:00
parent ddec5d7fc9
commit 650737cda2
3 changed files with 61 additions and 4 deletions
+8 -3
View File
@@ -1,5 +1,7 @@
import { Suspense } from "react";
import { getDashboardStats } from "@/lib/dashboard-queries";
import { Users, FolderOpen, Euro, Clock } from "lucide-react";
import { FollowUpWidget } from "@/components/admin/dashboard/FollowUpWidget";
export const revalidate = 0;
@@ -56,11 +58,11 @@ export default async function AdminDashboard() {
const { kpi, activity } = await getDashboardStats();
return (
<div className="max-w-5xl">
<div className="max-w-7xl">
<h1 className="text-2xl font-bold text-gray-900 mb-6">Dashboard</h1>
{/* KPI cards */}
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
{/* KPI cards + Follow-up Widget */}
<div className="grid grid-cols-2 lg:grid-cols-5 gap-4 mb-8">
<KpiCard
label="Clienti attivi"
value={kpi.clientiAttivi}
@@ -87,6 +89,9 @@ export default async function AdminDashboard() {
icon={Clock}
color="bg-amber-500"
/>
<Suspense fallback={<div className="animate-pulse">Loading...</div>}>
<FollowUpWidget />
</Suspense>
</div>
{/* Activity feed */}
@@ -0,0 +1,49 @@
import { getLeadsNeedingFollowUp } from "@/lib/lead-service";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { AlertCircle } from "lucide-react";
import Link from "next/link";
export async function FollowUpWidget() {
const leadsNeedingFollowUp = await getLeadsNeedingFollowUp(7); // 7 days
return (
<Card className="border-orange-200 bg-orange-50">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<AlertCircle className="h-5 w-5 text-orange-600" />
Richiesta Follow-up
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
{leadsNeedingFollowUp.length > 0 ? (
<>
<p className="text-lg font-bold text-orange-900">
{leadsNeedingFollowUp.length} lead
{leadsNeedingFollowUp.length !== 1 ? "s" : ""} da contattare
</p>
<ul className="space-y-2 text-sm">
{leadsNeedingFollowUp.slice(0, 3).map((lead) => (
<li key={lead.id} className="flex justify-between items-center">
<span>{lead.name}</span>
<Button variant="ghost" size="sm" asChild>
<Link href={`/admin/leads/${lead.id}`}>Contatta</Link>
</Button>
</li>
))}
</ul>
{leadsNeedingFollowUp.length > 3 && (
<Button variant="outline" className="w-full" asChild>
<Link href="/admin/leads">
Visualizza Tutti ({leadsNeedingFollowUp.length})
</Link>
</Button>
)}
</>
) : (
<p className="text-gray-600 text-sm">Tutti i lead sono aggiornati!</p>
)}
</CardContent>
</Card>
);
}
+4 -1
View File
@@ -7,6 +7,8 @@ import { Button } from "@/components/ui/button";
import { formatDistanceToNow, format } from "date-fns";
import { it } from "date-fns/locale";
import { EditLeadModal } from "./LeadForm";
import { LogActivityModal } from "./LogActivityModal";
import { SendQuoteModal } from "./SendQuoteModal";
const ACTIVITY_ICON: Record<string, string> = {
call: "📞",
@@ -43,7 +45,8 @@ export function LeadDetail({
</div>
<div className="flex gap-2">
<EditLeadModal lead={lead} />
<Button variant="outline">Log Activity</Button>
<LogActivityModal leadId={lead.id} />
<SendQuoteModal leadId={lead.id} />
</div>
</div>