lets create another tab on the admin dashboard called "leads" where you
This commit is contained in:
@@ -23,7 +23,8 @@ import {
|
|||||||
LogOut,
|
LogOut,
|
||||||
Code2,
|
Code2,
|
||||||
Mails,
|
Mails,
|
||||||
Send
|
Send,
|
||||||
|
ClipboardList
|
||||||
} from "lucide-react"
|
} from "lucide-react"
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||||
@@ -52,6 +53,14 @@ function AdminLayout({
|
|||||||
</SidebarMenuButton>
|
</SidebarMenuButton>
|
||||||
</Link>
|
</Link>
|
||||||
</SidebarMenuItem>
|
</SidebarMenuItem>
|
||||||
|
<SidebarMenuItem>
|
||||||
|
<Link href="/admin/leads">
|
||||||
|
<SidebarMenuButton tooltip="Leads">
|
||||||
|
<ClipboardList />
|
||||||
|
<span>Leads</span>
|
||||||
|
</SidebarMenuButton>
|
||||||
|
</Link>
|
||||||
|
</SidebarMenuItem>
|
||||||
<SidebarMenuItem>
|
<SidebarMenuItem>
|
||||||
<Link href="/admin/embed">
|
<Link href="/admin/embed">
|
||||||
<SidebarMenuButton tooltip="Embed">
|
<SidebarMenuButton tooltip="Embed">
|
||||||
|
|||||||
@@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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 [];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -109,6 +109,16 @@ async function createEstimatePdf(data: InputType): Promise<Buffer> {
|
|||||||
return Buffer.from(pdfBytes);
|
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 }> {
|
export async function sendEstimateEmail(data: InputType): Promise<{ success: boolean, message?: string }> {
|
||||||
const validation = inputSchema.safeParse(data);
|
const validation = inputSchema.safeParse(data);
|
||||||
@@ -148,6 +158,10 @@ export async function sendEstimateEmail(data: InputType): Promise<{ success: boo
|
|||||||
};
|
};
|
||||||
|
|
||||||
await transporter.sendMail(mailOptions);
|
await transporter.sendMail(mailOptions);
|
||||||
|
|
||||||
|
// Save lead after email is sent successfully
|
||||||
|
await saveLead(validation.data.name, validation.data.email);
|
||||||
|
|
||||||
return { success: true };
|
return { success: true };
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
import Database from 'better-sqlite3';
|
import Database from 'better-sqlite3';
|
||||||
|
|
||||||
// Use a file-based database in development
|
// 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;
|
export default db;
|
||||||
|
|||||||
Reference in New Issue
Block a user