fh / src /components /FinalDownload.svelte
Varun10000's picture
Upload 57 files
d8635c9 verified
Raw
History Blame Contribute Delete
15 kB
<script lang="ts">
import { rfpData, prevStep, resetApp } from '../stores/appStore'
import { generateWordDocument, generateDocumentPreview } from '../utils/documentGenerator'
$: answers = $rfpData.answers
$: totalQuestions = answers.length
$: finalizedAnswers = answers.filter(a => a.isFinalized)
let isGenerating = false
let downloadUrl: string | null = null
let showPreview = false
let previewContent = ''
function openPreview() {
previewContent = generateDocumentPreview(answers, {
title: 'RFP Response Document',
company: 'SEDNA Consulting Group',
date: new Date().toLocaleDateString(),
generatedBy: 'RFP Management System',
rfpName: $rfpData.currentRFP?.rfpName,
bidNumber: $rfpData.currentRFP?.bidNumber
})
showPreview = true
}
function closePreview() {
showPreview = false
previewContent = ''
}
async function downloadWordDocument() {
isGenerating = true
try {
const blob = await generateWordDocument(answers, {
title: 'RFP Response Document',
company: 'SEDNA Consulting Group',
date: new Date().toLocaleDateString(),
generatedBy: 'RFP Management System',
rfpName: $rfpData.currentRFP?.rfpName,
bidNumber: $rfpData.currentRFP?.bidNumber
})
downloadUrl = URL.createObjectURL(blob)
// Generate filename with RFP details
const currentRfp = $rfpData.currentRFP
let filename = 'RFP_Response'
if (currentRfp?.bidNumber) {
filename += `_${currentRfp.bidNumber.replace(/[^a-zA-Z0-9]/g, '_')}`
} else if (currentRfp?.rfpName) {
filename += `_${currentRfp.rfpName.substring(0, 30).replace(/[^a-zA-Z0-9]/g, '_')}`
}
filename += `_${new Date().toISOString().split('T')[0]}.docx`
// Trigger download
const link = document.createElement('a')
link.href = downloadUrl
link.download = filename
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
} catch (error) {
console.error('Error generating document:', error)
alert('Error generating document. Please try again.')
} finally {
isGenerating = false
}
}
function startNew() {
if (downloadUrl) {
URL.revokeObjectURL(downloadUrl)
}
resetApp()
}
function getCompletionPercentage(): number {
return Math.round((finalizedAnswers.length / totalQuestions) * 100)
}
</script>
<div class="max-w-5xl mx-auto">
<div class="bg-gray-800 border border-gray-600 rounded-lg shadow-lg p-8">
<!-- Success Header -->
<div class="text-center mb-8">
<div class="mx-auto flex items-center justify-center h-16 w-16 rounded-full bg-green-900/20 mb-4">
<svg class="h-8 w-8 text-green-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
</svg>
</div>
<h2 class="text-3xl font-bold text-white mb-2">
RFP Response Complete!
</h2>
<p class="text-gray-300">
Your AI-enhanced RFP responses are ready for download
</p>
</div>
<!-- RFP Information -->
{#if $rfpData.currentRFP?.rfpName || $rfpData.currentRFP?.bidNumber}
<div class="bg-blue-900/10 border border-blue-800/30 rounded-lg p-6 mb-8">
<h3 class="text-lg font-semibold text-blue-300 mb-3">RFP Details</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
{#if $rfpData.currentRFP?.rfpName}
<div>
<div class="text-sm text-blue-200 mb-1">RFP Name</div>
<div class="text-white font-medium">{$rfpData.currentRFP.rfpName}</div>
</div>
{/if}
{#if $rfpData.currentRFP?.bidNumber}
<div>
<div class="text-sm text-blue-200 mb-1">Bid Number</div>
<div class="text-white font-medium">{$rfpData.currentRFP.bidNumber}</div>
</div>
{/if}
</div>
<p class="text-blue-300/80 text-sm mt-3">This information will be included in the cover letter of your final document.</p>
</div>
{/if}
<!-- Summary Stats -->
<div class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8">
<div class="bg-blue-900/20 p-6 rounded-lg border border-blue-800">
<div class="text-3xl font-bold text-blue-400 mb-2">{totalQuestions}</div>
<div class="text-blue-200 font-medium">Total Questions</div>
<div class="text-blue-400 text-sm">Answered</div>
</div>
<div class="bg-green-900/20 p-6 rounded-lg border border-green-800">
<div class="text-3xl font-bold text-green-400 mb-2">{finalizedAnswers.length}</div>
<div class="text-green-200 font-medium">Finalized</div>
<div class="text-green-400 text-sm">Ready for submission</div>
</div>
<div class="bg-purple-900/20 p-6 rounded-lg border border-purple-800">
<div class="text-3xl font-bold text-purple-400 mb-2">
{finalizedAnswers.reduce((sum, answer) => sum + (answer.citations?.length || 0), 0)}
</div>
<div class="text-purple-200 font-medium">Citations</div>
<div class="text-purple-400 text-sm">From previous RFPs</div>
</div>
<div class="bg-orange-900/20 p-6 rounded-lg border border-orange-800">
<div class="text-3xl font-bold text-orange-400 mb-2">{getCompletionPercentage()}%</div>
<div class="text-orange-200 font-medium">Complete</div>
<div class="text-orange-400 text-sm">Ready to download</div>
</div>
</div>
<!-- Document Preview -->
<div class="mb-8">
<h3 class="text-xl font-semibold text-white mb-4">Document Preview</h3>
<div class="bg-gray-700 rounded-lg p-6 border border-gray-600">
<div class="flex items-center mb-4">
<svg class="w-8 h-8 text-blue-400 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 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>
<div>
<div class="font-semibold text-white">RFP Response Document</div>
<div class="text-sm text-gray-400">
Generated on {new Date().toLocaleDateString()} • Microsoft Word Format (.docx)
</div>
</div>
</div>
</div>
</div>
<!-- Answer Summary -->
<div class="mb-8">
<h3 class="text-xl font-semibold text-white mb-4">Answer Summary</h3>
<div class="space-y-3 max-h-64 overflow-y-auto">
{#each answers as answer, index}
<div class="flex items-center justify-between p-3 bg-gray-700 rounded-lg border border-gray-600">
<div class="flex-1">
<div class="font-medium text-white text-sm">
Question {index + 1}
</div>
<div class="text-gray-400 text-sm truncate">
{answer.question}
</div>
</div>
<div class="flex items-center space-x-3">
{#if answer.citations && answer.citations.length > 0}
<div class="flex items-center text-xs text-blue-400">
<svg class="w-3 h-3 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.746 0 3.332.477 4.5 1.253v13C19.832 18.477 18.246 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
</svg>
{answer.citations.length}
</div>
{/if}
<div class="w-6 h-6 rounded-full flex items-center justify-center {
answer.isFinalized
? 'bg-green-900/20'
: 'bg-gray-600'
}">
{#if answer.isFinalized}
<svg class="w-3 h-3 text-green-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
</svg>
{/if}
</div>
</div>
</div>
{/each}
</div>
</div>
<!-- Download Section -->
<div class="bg-blue-900/20 rounded-lg p-6 border border-blue-800 mb-8">
<h3 class="text-lg font-semibold text-blue-100 mb-4">
Ready to Download
</h3>
<p class="text-blue-200 mb-6">
Your professionally formatted RFP response document is ready.
</p>
<div class="flex flex-col sm:flex-row gap-4">
<button
class="flex-1 px-6 py-4 bg-purple-600 text-white rounded-lg font-medium hover:bg-purple-700 transition-colors flex items-center justify-center"
on:click={openPreview}
>
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
Preview Document
</button>
<button
class="flex-1 px-6 py-4 bg-blue-600 text-white rounded-lg font-medium hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors flex items-center justify-center"
disabled={isGenerating || finalizedAnswers.length === 0}
on:click={downloadWordDocument}
>
{#if isGenerating}
<div class="animate-spin rounded-full h-5 w-5 border-b-2 border-white mr-3"></div>
Generating Document...
{:else}
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="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>
Download Word Document
{/if}
</button>
</div>
</div>
<!-- Navigation Buttons -->
<div class="flex justify-between">
<button
class="px-6 py-3 bg-gray-700 text-gray-300 rounded-lg font-medium hover:bg-gray-600 transition-colors"
on:click={prevStep}
disabled={isGenerating}
>
<svg class="w-5 h-5 mr-2 inline" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
Back to Review
</button>
<button
class="px-6 py-3 bg-green-600 text-white rounded-lg font-medium hover:bg-green-700 transition-colors"
on:click={startNew}
disabled={isGenerating}
>
Start New RFP
<svg class="w-5 h-5 ml-2 inline" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
</button>
</div>
</div>
</div>
<!-- Preview Modal -->
{#if showPreview}
<div class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black bg-opacity-75">
<div class="bg-gray-800 rounded-lg shadow-2xl w-full max-w-6xl h-5/6 flex flex-col">
<!-- Modal Header -->
<div class="flex items-center justify-between p-6 border-b border-gray-600">
<div>
<h3 class="text-xl font-semibold text-white">Document Preview</h3>
<p class="text-gray-400 text-sm">Review your RFP response before downloading</p>
</div>
<div class="flex items-center space-x-3">
<button
class="px-4 py-2 bg-blue-600 text-white rounded-lg font-medium hover:bg-blue-700 transition-colors flex items-center"
on:click={downloadWordDocument}
>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="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>
Download
</button>
<button
class="p-2 text-gray-400 hover:text-white transition-colors"
on:click={closePreview}
>
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
</div>
<!-- Modal Content -->
<div class="flex-1 overflow-hidden">
<div class="h-full overflow-y-auto p-6">
<div class="bg-white rounded-lg p-8 text-gray-900 font-mono text-sm leading-relaxed shadow-inner">
{@html previewContent}
</div>
</div>
</div>
<!-- Modal Footer -->
<div class="flex items-center justify-between p-6 border-t border-gray-600">
<div class="text-sm text-gray-400">
This preview shows how your document will appear when downloaded.
</div>
<div class="flex space-x-3">
<button
class="px-4 py-2 bg-gray-700 text-gray-300 rounded-lg font-medium hover:bg-gray-600 transition-colors"
on:click={closePreview}
>
Close Preview
</button>
<button
class="px-4 py-2 bg-blue-600 text-white rounded-lg font-medium hover:bg-blue-700 transition-colors flex items-center"
disabled={isGenerating}
on:click={downloadWordDocument}
>
{#if isGenerating}
<div class="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
Generating...
{:else}
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="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>
Download Word Document
{/if}
</button>
</div>
</div>
</div>
</div>
{/if}