<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
"use client"
import { useState } from "react";
import BarListChart from "../charts/barList";
import BarChartComponent from "../charts/barChart";
import LineCharts from "../charts/lineChart";
import { data, ChartData, Reports, Channels, Campaigns, chartsMetaData, chartsSet1 } from "../dummyData";
import { format } from 'date-fns';
import { Select, SelectItem, Button, Slider } from "@nextui-org/react";
import html2canvas from 'html2canvas';
import jsPDF from 'jspdf';
import 'jspdf-autotable';
const Report = () => {
const [value, setValue] = useState(0);
const [selectedChannels, setSelectedChannels] = useState<string[]>([]);
const [selectedCampaigns, setSelectedCampaigns] = useState<string[]>([]);
const [selectedReports, setSelectedReports] = useState<string[]>([]);
const [startDate, setStartDate] = useState(new Date('2023-01-01').getTime());
const [endDate, setEndDate] = useState(new Date('2023-12-31').getTime());
const [filteredData, setFilteredData] = useState<any[]>();
const handleChange = (event: any, isStart: boolean) => {
const newValue = +event.target.value;
if (isStart) {
setValue(newValue);
} else {
setValue(newValue);
}
applyFilters()
};
const formatDate = (timestamp: number) => {
return format(new Date(timestamp), 'MM/dd/yyyy');
};
const monthMilliseconds = 30.44 * 24 * 60 * 60 * 1000;
type DataEntry = {
channel: string;
campaign: string;
filterDate: any;
date: any;
view_through_rates: number;
avg_cpc: number;
clicks: number;
conversion_rate: number;
connection: number;
cost: number;
cost_conversion: number;
impression: number;
};
type FilterCriteria = {
channels: string[];
campaigns: string[];
startDate?: any;
endDate?: any;
};
const filterData = (DataResource: any[], filters: FilterCriteria): DataEntry[] => {
console.log(filters)
return DataResource.filter((entry) => {
if (filters.channels.length > 0 && !filters.channels.includes(entry.channel)) {
return false;
}
if (filters.campaigns.length > 0 && !filters.campaigns.includes(entry.campaign)) {
return false;
}
if (filters.startDate && filters.endDate) {
const filterDate = new Date(entry.filterDate);
const startDate = new Date(filters.startDate);
const endDate = new Date(filters.endDate);
if (filterDate < startDate || filterDate > endDate) {
return false;
}
}
return true;
});
};
const applyFilters = () => {
const filters: FilterCriteria = {
channels: selectedChannels,
campaigns: selectedCampaigns,
startDate,
endDate,
};
const filteredData = filterData(ChartData, filters);
setFilteredData(filteredData)
console.log(filteredData);
};
const handleSelect = (e: any) => {
if (e?.target?.name === "channels") {
setSelectedChannels(e?.target?.value)
} else if (e.target.name === "campaigns") {
setSelectedCampaigns(e?.target?.value)
} else if (e.target.name === "reports") {
setSelectedReports(e?.target?.value)
}
applyFilters()
}
const downloadPDF = async () => {
const doc = new jsPDF();
try {
// Dynamically import jsPDF if not already imported
const jsPDF = await import('jspdf');
// Function to add charts to PDF based on ID
const addChartsToPDF = async (containerId:any, chartIds:any) => {
const chartsContainer = document.getElementById(containerId);
if (chartsContainer) {
const chartDivs = chartsContainer.children;
let yOffset = 10;
for (let i = 0; i < chartDivs.length; i++) {
const chartDiv = chartDivs[i];
const id = chartDiv.id;
// Check if chart ID is in the reports array
if (!chartIds.includes(id)) {
continue; // Skip if ID is not in reports array
}
const canvas = chartDiv.querySelector('canvas');
if (!canvas) {
console.warn(`No canvas found for chart ${i} in container ${containerId}`);
continue;
}
// Get image data URL from canvas
const imgData = canvas.toDataURL('image/png');
const { width, height } = canvas.getBoundingClientRect();
// Add image to PDF
if (yOffset + height * 0.75 > doc.internal.pageSize.height - 20) {
doc.addPage();
yOffset = 10;
}
doc.addImage(imgData, 'PNG', 10, yOffset, width * 0.75, height * 0.75);
yOffset += height * 0.75 + 10; // Adjust vertical spacing
}
} else {
console.warn(`Charts container with ID ${containerId} not found.`);
}
};
// Extract chart IDs from reports array
const reportChartIds = ["cost_conversion"]
// Add charts from charts-container with IDs in reports array
await addChartsToPDF('charts-container', reportChartIds);
// Add charts from charts-set-1 with IDs in reports array
await addChartsToPDF('charts-set-1', reportChartIds);
// Save the PDF
doc.save('reports.pdf');
} catch (error) {
console.error('Error generating PDF:', error);
}
};
return (
<div className="container mx-auto px-4 mb-8">
<div>
<div className="h-px bg-gray-300" />
<h1 className="text-3xl font-bold mb-4 mt-4">Reports</h1>
<div className="h-px bg-gray-200 mb-4" />
<p className="text-xl mb-8">Welcome, Xue!</p>
</div>
<div className="mb-4 w-1/3 flex items-center mx-auto">
<label htmlFor="startDateLabel" className="mr-2">{formatDate(startDate)}</label>
<input
id="startDateLabel"
type="range"
className="w-full bg-orange-300 rounded-full"
min={startDate}
max={endDate}
step={monthMilliseconds}
value={value}
onChange={(event) => handleChange(event, true)}
title={formatDate(value)}
/>
<label htmlFor="startDateLabel" className="ml-2">{formatDate(new Date().getTime())}</label>
</div>
<section className="w-full py-12 md:py-16 lg:py-20">
<div className="container px-4 md:px-6">
<div className="grid gap-6 md:grid-cols-[1fr_auto] items-start">
<div className="grid gap-4">
<div className="grid md:grid-cols-3 gap-4">
<Select
name="reports"
label="Select Report(s)"
placeholder="Select Report(s)"
selectionMode="multiple"
className="max-w-xs"
value={selectedReports}
onChange={(e: any) => { handleSelect(e) }}
>
{Reports.map((report) => (
<SelectItem key={report.key}>
{report.label}
</SelectItem>
))}
</Select>
<Select
name="channels"
label="Select Channel(s)"
placeholder="Select Channel(s)"
selectionMode="multiple"
className="max-w-xs"
value={selectedChannels}
onChange={(e: any) => { handleSelect(e) }}
>
{Channels.map((channel) => (
<SelectItem key={channel.key}>
{channel.label}
</SelectItem>
))}
</Select>
<Select
name="campaigns"
label="Select Campaign(s)"
placeholder="Select Campaign(s)"
selectionMode="multiple"
className="max-w-xs"
value={selectedCampaigns}
onChange={(e: any) => { handleSelect(e) }}
>
{Campaigns.map((campaign) => (
<SelectItem key={campaign.key}>
{campaign.label}
</SelectItem>
))}
</Select>
</div>
</div>
</div>
<div className="flex mt-4">
<Button className="shrink-0 bg-[#ff7f00] text-white" onClick={() => downloadPDF()}>Export</Button>
</div>
</div>
</section>
<div className="grid grid-cols-4 gap-2" id="charts-container">
{chartsMetaData.map((chart, index) => (
<div key={index} id={chart.categories[0]}>
<LineCharts
title={chart.title}
subTitle={chart.subTitle}
data={filteredData?.length ? filteredData : ChartData}
categories={chart.categories}
colors={chart.colors}
showLegend={true}
showYAxis={true}
startEndOnly={false}
/>
</div>
))}
</div>
<div className="grid grid-cols-4 gap-2 mt-4" id="charts-set-1">
{chartsSet1.map((chart, index) => (
<div key={index} id={chart.categories[0]}>
<LineCharts
title={chart.title}
subTitle={chart.subTitle}
data={filteredData?.length ? filteredData : ChartData}
categories={chart.categories}
colors={chart.colors}
showLegend={true}
showYAxis={true}
startEndOnly={false}
/>
</div>
))}
</div>
</div>
);
};
export default Report;
hello guys ,
Download PDF is not working here i want to download selected PDF using Js PDF ,next Js and Tremor charts. here when i am trying to download the PDF it is coming blank pages
what issue can be there. i have try using htmltocanvas as well but no luck . i want to download each chart on a single page so if there is 5 chart there will be 5 pages in pdf