Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 68 additions & 1 deletion src/app/analytics/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,88 @@
"use client";

import SubNavigation from "@/components/layout/SubNavigation";
import { downloadAnalyticsPDF } from "@/common/api/analytics/provider";
import { useState } from "react";

type AnalyticsLayoutProps = {
children: React.ReactNode;
};

export default function AnalyticsLayout({ children }: AnalyticsLayoutProps) {
const [isDownloading, setIsDownloading] = useState(false);

const navItems = [
{ label: "Summary", href: "/analytics/summary" },
{ label: "Attendance", href: "/analytics/attendance" },
{ label: "Events", href: "/analytics/events" },
{ label: "Organizers", href: "/analytics/organizers" },
];

const handleExportPDF = async () => {
setIsDownloading(true);
try {
await downloadAnalyticsPDF();
} catch (error) {
console.error("Failed to download PDF:", error);
alert("Failed to download PDF. Please try again.");
} finally {
setIsDownloading(false);
}
};

return (
<section className="space-y-6">
<header>
<header className="flex items-center justify-between">
<h1 className="text-2xl font-semibold text-zinc-900">Analytics</h1>
<button
onClick={handleExportPDF}
disabled={isDownloading}
className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed"
>
{isDownloading ? (
<>
<svg
className="animate-spin -ml-1 mr-2 h-4 w-4 text-white"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
></circle>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
Exporting...
</>
) : (
<>
<svg
className="-ml-1 mr-2 h-4 w-4"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
/>
</svg>
Export to PDF
</>
)}
</button>
</header>
<SubNavigation items={navItems} baseHref="/analytics" />
{children}
Expand Down
27 changes: 27 additions & 0 deletions src/common/api/analytics/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,30 @@ export async function getOrganizerScans(): Promise<AnalyticsScansResponse[]> {
method: "GET",
});
}

export async function downloadAnalyticsPDF(): Promise<void> {
const response = await fetch(
`${process.env.NEXT_PUBLIC_API_BASE_URL}/analytics/pdf`,
{
method: "GET",
credentials: "include",
headers: {
"Content-Type": "application/pdf",
},
}
);

if (!response.ok) {
throw new Error("Failed to download PDF");
}

const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = `analytics-report-${new Date().toISOString().split("T")[0]}.pdf`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
}