From 72c644f1c4d7e872efdc60d7d3fa3a632977aad6 Mon Sep 17 00:00:00 2001 From: Leon Serfaty G Date: Fri, 18 Jul 2025 04:13:46 +0000 Subject: [PATCH] lets create another tab on the admin dashboard called "leads" where you --- src/app/admin/layout.tsx | 11 ++++- src/app/admin/leads/page.tsx | 71 ++++++++++++++++++++++++++++++++ src/lib/actions/leads.ts | 24 +++++++++++ src/lib/actions/send-estimate.ts | 14 +++++++ src/lib/db.ts | 10 +++++ 5 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 src/app/admin/leads/page.tsx create mode 100644 src/lib/actions/leads.ts diff --git a/src/app/admin/layout.tsx b/src/app/admin/layout.tsx index 2652de4..c273de3 100644 --- a/src/app/admin/layout.tsx +++ b/src/app/admin/layout.tsx @@ -23,7 +23,8 @@ import { LogOut, Code2, Mails, - Send + Send, + ClipboardList } from "lucide-react" import { Button } from "@/components/ui/button"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; @@ -52,6 +53,14 @@ function AdminLayout({ + + + + + Leads + + + diff --git a/src/app/admin/leads/page.tsx b/src/app/admin/leads/page.tsx new file mode 100644 index 0000000..e9151b9 --- /dev/null +++ b/src/app/admin/leads/page.tsx @@ -0,0 +1,71 @@ + +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from '@/components/ui/card'; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from '@/components/ui/table'; +import { getLeads } from '@/lib/actions/leads'; +import { format } from 'date-fns'; + +export default async function LeadsPage() { + const leads = await getLeads(); + + return ( +
+
+

Leads

+

+ A list of all the users who have requested an estimate. +

+
+ + + Captured Leads + + Potential clients who completed the estimation form. + + + + + + + Name + Email + Date Submitted + + + + {leads.length > 0 ? ( + leads.map((lead) => ( + + {lead.name} + {lead.email} + + {format(new Date(lead.createdAt), 'PPP p')} + + + )) + ) : ( + + + No leads yet. + + + )} + +
+
+
+
+ ); +} diff --git a/src/lib/actions/leads.ts b/src/lib/actions/leads.ts new file mode 100644 index 0000000..d7b6c83 --- /dev/null +++ b/src/lib/actions/leads.ts @@ -0,0 +1,24 @@ + +'use server'; + +import db from '@/lib/db'; + +export type Lead = { + id: number; + name: string; + email: string; + createdAt: string; +}; + +export async function getLeads(): Promise { + try { + const stmt = db.prepare( + 'SELECT id, name, email, createdAt FROM leads ORDER BY createdAt DESC' + ); + const leads = stmt.all() as Lead[]; + return leads; + } catch (error) { + console.error('Failed to fetch leads:', error); + return []; + } +} diff --git a/src/lib/actions/send-estimate.ts b/src/lib/actions/send-estimate.ts index 1a2beb3..cca01e3 100644 --- a/src/lib/actions/send-estimate.ts +++ b/src/lib/actions/send-estimate.ts @@ -109,6 +109,16 @@ async function createEstimatePdf(data: InputType): Promise { return Buffer.from(pdfBytes); } +async function saveLead(name: string, email: string) { + try { + const stmt = db.prepare('INSERT INTO leads (name, email) VALUES (?, ?)'); + stmt.run(name, email); + console.log(`Lead saved: ${name}, ${email}`); + } catch (error) { + console.error('Failed to save lead:', error); + // We don't want to block email sending if lead saving fails, so we just log the error. + } +} export async function sendEstimateEmail(data: InputType): Promise<{ success: boolean, message?: string }> { const validation = inputSchema.safeParse(data); @@ -148,6 +158,10 @@ export async function sendEstimateEmail(data: InputType): Promise<{ success: boo }; await transporter.sendMail(mailOptions); + + // Save lead after email is sent successfully + await saveLead(validation.data.name, validation.data.email); + return { success: true }; } catch (error: any) { diff --git a/src/lib/db.ts b/src/lib/db.ts index a83f641..a88a00b 100644 --- a/src/lib/db.ts +++ b/src/lib/db.ts @@ -1,3 +1,4 @@ + import Database from 'better-sqlite3'; // Use a file-based database in development @@ -28,5 +29,14 @@ db.exec(` ) `); +db.exec(` + CREATE TABLE IF NOT EXISTS leads ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + email TEXT NOT NULL, + createdAt DATETIME DEFAULT CURRENT_TIMESTAMP + ) +`); + export default db;