Boopster's picture
feat: implement trend chart PDF export and doctor report sharing functionality with doctor details.
d8b81da
"use client";
import React from "react";
import { BarChart3, TrendingUp, AlertTriangle, Sparkles, Download, Send, Loader2, AlertCircle } from "lucide-react";
interface TrendData {
day: string;
count: number;
}
interface TrendChartProps {
data: TrendData[];
total_episodes: number;
worst_day: string;
insight?: string;
doctorName?: string;
doctorEmail?: string;
onSend?: () => void;
}
export function TrendChart({ data, total_episodes, worst_day, insight, doctorName, doctorEmail, onSend }: TrendChartProps) {
const maxCount = Math.max(...data.map(d => d.count), 1);
const canSend = !!doctorEmail;
const [isExporting, setIsExporting] = React.useState(false);
const handleExportPDF = async () => {
if (isExporting) return;
setIsExporting(true);
try {
const html2pdf = (await import("html2pdf.js")).default;
const dateStr = new Date().toLocaleDateString("en-GB", {
day: "numeric",
month: "long",
year: "numeric",
});
const rows = data.map(d =>
`<tr><td style="padding: 8px 16px; border-bottom: 1px solid #eee;">${d.day}</td><td style="padding: 8px 16px; border-bottom: 1px solid #eee; text-align: center; font-weight: 600;">${d.count}</td></tr>`
).join("");
const container = document.createElement("div");
container.innerHTML = `
<div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; max-width: 700px; margin: 0 auto; padding: 32px; color: #1a1a1a; line-height: 1.8;">
<h1 style="font-size: 1.5rem; border-bottom: 2px solid #00d4aa; padding-bottom: 8px; margin-bottom: 4px;">Headache Trend Analysis</h1>
<p style="color: #666; font-size: 0.85rem; margin-bottom: 24px;">Generated ${dateStr}${doctorName ? ` · For ${doctorName}` : ""}</p>
<h2 style="font-size: 1.2rem; margin-top: 2rem; color: #333;">Overview</h2>
<p><strong>Total episodes (30 days):</strong> ${total_episodes}</p>
<p><strong>Peak trigger day:</strong> ${worst_day}</p>
<h2 style="font-size: 1.2rem; margin-top: 2rem; color: #333;">Episodes by Day of Week</h2>
<table style="width: 100%; border-collapse: collapse; margin-top: 12px;">
<thead><tr style="background: #f5f5f5;"><th style="padding: 8px 16px; text-align: left; font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.05em; color: #555;">Day</th><th style="padding: 8px 16px; text-align: center; font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.05em; color: #555;">Episodes</th></tr></thead>
<tbody>${rows}</tbody>
</table>
${insight ? `<h2 style="font-size: 1.2rem; margin-top: 2rem; color: #333;">AI Insight</h2><p style="font-style: italic;">"${insight}"</p>` : ""}
</div>
`;
await html2pdf()
.set({
margin: [10, 10, 10, 10],
filename: `Headache_Trends_${new Date().toISOString().slice(0, 10)}.pdf`,
image: { type: "jpeg", quality: 0.98 },
html2canvas: { scale: 2, useCORS: true },
jsPDF: { unit: "mm", format: "a4", orientation: "portrait" },
})
.from(container)
.save();
} catch (err) {
console.error("PDF export failed:", err);
} finally {
setIsExporting(false);
}
};
return (
<div className="bg-surface-elevated/40 border border-surface-overlay rounded-2xl shadow-2xl relative overflow-hidden group flex flex-col">
<div className="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-accent-pink/20 via-accent-cyan to-cta/20 opacity-30" />
<div className="p-6 flex-1">
<div className="flex items-center justify-between mb-8">
<div className="flex items-center gap-3">
<div className="p-2 bg-accent-cyan/10 text-accent-cyan rounded-xl border border-accent-cyan/20">
<BarChart3 className="w-5 h-5" />
</div>
<div>
<h3 className="text-base font-bold text-primary tracking-tight">Cerebral Patterns</h3>
<p className="text-[10px] text-muted uppercase tracking-[0.15em] font-bold">Last 7 Reporting Periods</p>
</div>
</div>
<div className="px-3 py-1 bg-surface-overlay rounded-lg border border-surface-overlay flex flex-col items-center">
<span className="text-[10px] text-muted uppercase font-black tracking-tighter">Events</span>
<span className="text-sm font-bold text-accent-cyan leading-none">{total_episodes}</span>
</div>
</div>
{/* Bar Chart */}
<div className="flex items-end justify-between gap-3 h-32 mb-8 px-1">
{data.map((item) => {
const height = (item.count / maxCount) * 100;
const isWorst = item.day === worst_day;
return (
<div key={item.day} className="flex-1 flex flex-col items-center gap-3 group/bar">
<div className="w-full relative flex flex-col justify-end h-full">
<div
className={`
w-full rounded-t-lg transition-all duration-700 ease-out
${isWorst
? 'bg-accent-pink shadow-[0_0_20px_rgba(255,107,157,0.3)]'
: 'bg-surface-overlay group-hover/bar:bg-accent-cyan/40'
}
`}
style={{ height: `${Math.max(height, 5)}%` }}
/>
{item.count > 0 && (
<span className={`
absolute -top-7 left-1/2 -translate-x-1/2 text-[9px] font-black px-1.5 py-0.5 rounded border transition-all duration-300
${isWorst
? 'bg-accent-pink text-black border-accent-pink scale-110'
: 'bg-surface-elevated text-muted border-surface-overlay opacity-0 group-hover/bar:opacity-100'
}
`}>
{item.count}
</span>
)}
</div>
<span className={`text-[9px] font-black uppercase tracking-[0.15em] ${isWorst ? 'text-accent-pink' : 'text-muted'}`}>
{item.day.substring(0, 3)}
</span>
</div>
);
})}
</div>
{/* Insights */}
<div className="grid grid-cols-2 gap-4">
<div className="bg-surface-subtle/60 rounded-xl p-4 border border-surface-overlay flex flex-col gap-1">
<div className="flex items-center gap-2 text-accent-pink opacity-80">
<AlertTriangle className="w-3.5 h-3.5" />
<span className="text-[9px] font-black uppercase tracking-widest">Peak Triggers</span>
</div>
<p className="text-xs text-primary font-bold">{worst_day}</p>
</div>
<div className="bg-surface-subtle/60 rounded-xl p-4 border border-surface-overlay flex flex-col gap-1">
<div className="flex items-center gap-2 text-accent-cyan opacity-80">
<TrendingUp className="w-3.5 h-3.5" />
<span className="text-[9px] font-black uppercase tracking-widest">Trend Status</span>
</div>
<p className="text-xs text-primary font-bold">Stable Analysis</p>
</div>
{insight && (
<div className="col-span-2 bg-cta/5 rounded-2xl p-4 border border-cta/20 flex gap-4 items-start group/insight hover:bg-cta/10 transition-colors">
<div className="w-8 h-8 rounded-lg bg-cta/20 flex items-center justify-center shrink-0">
<Sparkles className="w-4 h-4 text-cta" />
</div>
<div className="space-y-1">
<p className="text-[10px] font-black uppercase tracking-[0.2em] text-cta/70">AI Correlation</p>
<p className="text-sm text-primary leading-[1.6] italic">
&quot;{insight}&quot;
</p>
</div>
</div>
)}
</div>
</div>
{/* Footer / Actions */}
<div className="px-6 py-5 bg-surface-subtle border-t border-surface-overlay flex flex-col gap-4">
<div className="flex items-center gap-4">
<button
onClick={handleExportPDF}
disabled={isExporting}
className="flex-1 flex items-center justify-center gap-2 px-4 py-3 bg-surface-elevated border border-surface-overlay hover:border-text-muted disabled:opacity-30 rounded-xl text-[11px] font-bold text-secondary transition-all"
>
{isExporting ? <Loader2 className="w-4 h-4 animate-spin" /> : <Download className="w-4 h-4" />}
{isExporting ? "Generating..." : "Export PDF"}
</button>
<button
onClick={onSend}
disabled={!canSend}
className={`
flex-[1.5] flex items-center justify-center gap-2 px-6 py-3 rounded-xl text-[11px] font-black uppercase tracking-widest transition-all shadow-xl
${canSend
? 'bg-cta text-black hover:shadow-cta/20 active:scale-95'
: 'bg-surface-overlay text-muted opacity-50'
}
`}
>
<Send className="w-4 h-4" />
{doctorName ? `Send to ${doctorName}` : "Send to Provider"}
</button>
</div>
{!doctorEmail && (
<div className="flex items-center gap-3 px-4 py-3 bg-accent-pink/5 border border-accent-pink/10 rounded-xl">
<AlertCircle className="w-4 h-4 text-accent-pink shrink-0" />
<p className="text-[10px] text-accent-pink font-medium leading-tight">
<strong>Action Required:</strong> Add your neurologist&apos;s email in <span className="underline cursor-pointer">Settings</span> to enable direct clinical sharing.
</p>
</div>
)}
</div>
</div>
);
}