lets create another tab on the admin dashboard called "leads" where you

This commit is contained in:
Leon Serfaty G
2025-07-18 04:13:46 +00:00
parent a8eb3376fc
commit 72c644f1c4
5 changed files with 129 additions and 1 deletions
+10 -1
View File
@@ -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({
</SidebarMenuButton>
</Link>
</SidebarMenuItem>
<SidebarMenuItem>
<Link href="/admin/leads">
<SidebarMenuButton tooltip="Leads">
<ClipboardList />
<span>Leads</span>
</SidebarMenuButton>
</Link>
</SidebarMenuItem>
<SidebarMenuItem>
<Link href="/admin/embed">
<SidebarMenuButton tooltip="Embed">
+71
View File
@@ -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 (
<div className="space-y-8">
<div>
<h1 className="text-3xl font-bold tracking-tight">Leads</h1>
<p className="mt-2 text-muted-foreground">
A list of all the users who have requested an estimate.
</p>
</div>
<Card>
<CardHeader>
<CardTitle>Captured Leads</CardTitle>
<CardDescription>
Potential clients who completed the estimation form.
</CardDescription>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>
<TableHead>Email</TableHead>
<TableHead className="text-right">Date Submitted</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{leads.length > 0 ? (
leads.map((lead) => (
<TableRow key={lead.id}>
<TableCell className="font-medium">{lead.name}</TableCell>
<TableCell>{lead.email}</TableCell>
<TableCell className="text-right">
{format(new Date(lead.createdAt), 'PPP p')}
</TableCell>
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={3} className="text-center h-24">
No leads yet.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</CardContent>
</Card>
</div>
);
}
+24
View File
@@ -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<Lead[]> {
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 [];
}
}
+14
View File
@@ -109,6 +109,16 @@ async function createEstimatePdf(data: InputType): Promise<Buffer> {
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) {
+10
View File
@@ -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;