diff --git a/src/app/api/reports/upload-pdf/route.ts b/src/app/api/reports/upload-pdf/route.ts
new file mode 100644
index 0000000..1c13143
--- /dev/null
+++ b/src/app/api/reports/upload-pdf/route.ts
@@ -0,0 +1,29 @@
+import { auth } from "@/lib/auth";
+import { uploadFile } from "@/lib/upload-file";
+
+export async function POST(request: Request) {
+ const session = await auth.api.getSession({ headers: request.headers });
+
+ if (!session) {
+ return Response.json({ error: "unauthorized" }, { status: 401 });
+ }
+
+ const body = await request.blob();
+ const pathname = `reports/${session.user.id}/preview-${Date.now()}.pdf`;
+
+ try {
+ await uploadFile(pathname, body, {
+ access: "private",
+ contentType: "application/pdf",
+ addRandomSuffix: true,
+ });
+ } catch (error) {
+ console.error("[reports/upload-pdf] uploadFile failed", error);
+ return Response.json(
+ { error: "upload failed" },
+ { status: 500 }
+ );
+ }
+
+ return Response.json({ success: true });
+}
diff --git a/src/app/api/reports/upload/route.ts b/src/app/api/reports/upload/route.ts
new file mode 100644
index 0000000..5711294
--- /dev/null
+++ b/src/app/api/reports/upload/route.ts
@@ -0,0 +1,26 @@
+import { auth } from "@/lib/auth";
+import { uploadFile } from "@/lib/upload-file";
+
+export async function POST(request: Request) {
+ const session = await auth.api.getSession({ headers: request.headers });
+
+ if (!session) {
+ return Response.json({ error: "unauthorized" }, { status: 401 });
+ }
+
+ const pathname = `reports/${session.user.id}/placeholder-${Date.now()}.txt`;
+ const body = "Report upload placeholder";
+
+ try {
+ await uploadFile(pathname, body, {
+ access: "private",
+ contentType: "text/plain",
+ addRandomSuffix: true,
+ });
+ } catch (error) {
+ console.error("[reports/upload] uploadFile failed (ignored for now)", error);
+ }
+
+ return Response.json({ success: true });
+}
+
diff --git a/src/app/preview/report-doc.tsx b/src/app/preview/report-doc.tsx
index 506f4dc..19beb38 100644
--- a/src/app/preview/report-doc.tsx
+++ b/src/app/preview/report-doc.tsx
@@ -1,15 +1,6 @@
"use client";
-import {
- Page,
- Text,
- View,
- Document,
- Image,
- StyleSheet,
- PDFViewer,
- Font,
-} from "@react-pdf/renderer";
+import { Page, Text, View, Document, Image, StyleSheet, PDFViewer, Font, pdf } from "@react-pdf/renderer";
import { ReactElement, useEffect, useState } from "react";
import dynamic from "next/dynamic";
import { LoaderCircle } from "lucide-react";
@@ -118,6 +109,45 @@ function ReportDoc({
setIsLoading(false);
}, [charts]);
+ // generate pdf blob and uplaod via API
+ const hasUploadedRef = useRef(false);
+ useEffect(() => {
+ if (isLoading || hasUploadedRef.current) return;
+
+ let cancelled = false;
+ (async () => {
+ const doc = (
+