feat(pdf): complete Epic 3 - PDF Export functionality

Story 3-1: Create PDF Generation API Route
- Add server/api/resume/pdf.get.ts with Puppeteer integration
- Support both dev (puppeteer) and prod (puppeteer-core + chromium)
- Navigate to /resume?print=true for WYSIWYG capture
- Return PDF with proper headers (Content-Type, Content-Disposition)
- Add error handling with 500 status and JSON response
- Update vercel.json with function config (memory: 1024, maxDuration: 10)

Story 3-2: Create PDF Download Composable
- Add app/composables/useResumePdf.ts
- Implement isGenerating ref for loading state
- Implement downloadPdf() with blob handling
- Add toast notifications for errors
- Revoke object URL to prevent memory leaks

Story 3-3: Connect Download Button to PDF Generation
- Update ResumeDownloadButton.vue to use useResumePdf()
- Bind :loading and :disabled to isGenerating
- Connect @click to downloadPdf
- Remove placeholder handler

Dependencies Added:
- puppeteer ^24.31.0
- puppeteer-core ^24.31.0
- @sparticuz/chromium ^141.0.0

Closes Epic 3
Closes Story 3-1, 3-2, 3-3
This commit is contained in:
mahdiarghyani
2025-12-01 12:20:30 +03:30
parent 3ecd1616e0
commit 43ad1bb5be
7 changed files with 12 additions and 17 deletions
@@ -5,17 +5,12 @@ interface Props {
defineProps<Props>()
function handleDownload() {
// Placeholder - will be replaced in Epic 3 Story 3.3
console.log('Download PDF clicked')
// Future: const { downloadPdf } = useResumePdf()
// Future: await downloadPdf()
}
const { isGenerating, downloadPdf } = useResumePdf()
</script>
<template>
<UButton v-if="!isPrintMode" icon="i-heroicons-arrow-down-tray" size="lg" color="primary"
class="fixed bottom-6 right-6 shadow-lg no-print z-50" @click="handleDownload">
<UButton v-if="!isPrintMode" icon="i-heroicons-arrow-down-tray" size="lg" color="primary" :loading="isGenerating"
:disabled="isGenerating" class="fixed bottom-6 right-6 shadow-lg no-print z-50" @click="downloadPdf">
<span class="hidden sm:inline">Download PDF</span>
</UButton>
</template>