feat: add working export buttons for Leads Finder (HTML download + CSV export)
- exportLeadsReport function: downloads HTML file or extracts table to CSV - Inline export buttons in chat messages when leads data exists - Canvas action bar export buttons for leads agent - HTML export: downloads full leads-report.html - CSV export: parses HTML table, extracts Name/Platform/Followers/Region/Bio/Link
This commit is contained in:
@@ -937,6 +937,50 @@ export default function AIAssist({ vibeMode = false }: { vibeMode?: boolean } =
|
|||||||
downloadSeoReport(seoAuditData, format);
|
downloadSeoReport(seoAuditData, format);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const exportLeadsReport = (format: "html" | "csv") => {
|
||||||
|
const htmlContent = previewData?.data;
|
||||||
|
if (!htmlContent) return;
|
||||||
|
if (format === "html") {
|
||||||
|
const blob = new Blob([htmlContent], { type: "text/html" });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.href = url;
|
||||||
|
a.download = "leads-report.html";
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
} else {
|
||||||
|
// Extract table rows from HTML and convert to CSV
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const doc = parser.parseFromString(htmlContent, "text/html");
|
||||||
|
const rows = doc.querySelectorAll("tbody tr");
|
||||||
|
if (rows.length === 0) return;
|
||||||
|
let csv = "Name,Platform,Followers,Region,Bio,Link\n";
|
||||||
|
rows.forEach((row) => {
|
||||||
|
const cells = row.querySelectorAll("td");
|
||||||
|
if (cells.length >= 7) {
|
||||||
|
const name = cells[1]?.textContent?.trim().replace(/,/g, ";") || "";
|
||||||
|
const platform = cells[2]?.textContent?.trim() || "";
|
||||||
|
const followers = cells[3]?.textContent?.trim() || "";
|
||||||
|
const region = cells[4]?.textContent?.trim().replace(/,/g, ";") || "";
|
||||||
|
const bio = cells[5]?.textContent?.trim().replace(/,/g, ";") || "";
|
||||||
|
const link = cells[6]?.querySelector("a")?.href || "";
|
||||||
|
csv += `"${name}","${platform}","${followers}","${region}","${bio}","${link}"\n`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const blob = new Blob([csv], { type: "text/csv" });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.href = url;
|
||||||
|
a.download = "leads-report.csv";
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const clearHistory = () => {
|
const clearHistory = () => {
|
||||||
updateActiveTab({
|
updateActiveTab({
|
||||||
@@ -1440,6 +1484,24 @@ export default function AIAssist({ vibeMode = false }: { vibeMode?: boolean } =
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{currentAgent === "leads" && previewData?.data && (
|
||||||
|
<div className="mt-2 flex gap-2 animate-in zoom-in-95 duration-300">
|
||||||
|
<Button
|
||||||
|
onClick={() => exportLeadsReport("html")}
|
||||||
|
variant="outline"
|
||||||
|
className="flex-1 bg-emerald-500/10 hover:bg-emerald-500/20 border-emerald-500/20 text-emerald-300 font-black uppercase text-[9px] tracking-wider py-3 rounded-xl min-w-0"
|
||||||
|
>
|
||||||
|
<Download className="h-3.5 w-3.5 mr-1" /> <span className="truncate">Export HTML</span>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => exportLeadsReport("csv")}
|
||||||
|
variant="outline"
|
||||||
|
className="flex-1 bg-blue-500/10 hover:bg-blue-500/20 border-blue-500/20 text-blue-300 font-black uppercase text-[9px] tracking-wider py-3 rounded-xl min-w-0"
|
||||||
|
>
|
||||||
|
<FileText className="h-3.5 w-3.5 mr-1" /> <span className="truncate">Export CSV</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{/* Inline SEO Export - always visible in chat when SEO data exists */}
|
{/* Inline SEO Export - always visible in chat when SEO data exists */}
|
||||||
@@ -1461,6 +1523,25 @@ export default function AIAssist({ vibeMode = false }: { vibeMode?: boolean } =
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{/* Inline Leads Export - always visible in chat when leads data exists */}
|
||||||
|
{msg.role === "assistant" && msg.agent === "leads" && msg.preview?.data && (
|
||||||
|
<div className="mt-3 flex gap-2 animate-in zoom-in-95 duration-300">
|
||||||
|
<Button
|
||||||
|
onClick={() => { setPreviewData(msg.preview as any); setTimeout(() => exportLeadsReport("html"), 100); }}
|
||||||
|
variant="outline"
|
||||||
|
className="flex-1 bg-emerald-500/10 hover:bg-emerald-500/20 border-emerald-500/30 text-emerald-300 font-black uppercase text-[9px] tracking-wider py-2.5 rounded-xl min-w-0"
|
||||||
|
>
|
||||||
|
<Download className="h-3 w-3 mr-1.5" /> <span className="truncate">Export HTML</span>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => { setPreviewData(msg.preview as any); setTimeout(() => exportLeadsReport("csv"), 100); }}
|
||||||
|
variant="outline"
|
||||||
|
className="flex-1 bg-blue-500/10 hover:bg-blue-500/20 border-blue-500/30 text-blue-300 font-black uppercase text-[9px] tracking-wider py-2.5 rounded-xl min-w-0"
|
||||||
|
>
|
||||||
|
<FileText className="h-3 w-3 mr-1.5" /> <span className="truncate">Export CSV</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{msg.role === "assistant" && isProcessing && i === aiAssistHistory.length - 1 && status && (
|
{msg.role === "assistant" && isProcessing && i === aiAssistHistory.length - 1 && status && (
|
||||||
|
|||||||
Reference in New Issue
Block a user