fh / src /components /UploadCurrentRFP.svelte
Varun10000's picture
Upload 57 files
d8635c9 verified
Raw
History Blame Contribute Delete
11.8 kB
<script lang="ts">
import { nextStep, currentRfp, type CurrentRFPDocument } from '../stores/appStore'
import { createEventDispatcher } from 'svelte'
const dispatch = createEventDispatcher()
let uploadedFile: File | null = null
let dragActive = false
let extractedContent = ''
let documentInfo: any = null
let isProcessing = false
let showFullContent = false
// RFP metadata fields
let rfpName = ''
let bidNumber = ''
async function handleFileUpload(event: Event) {
const target = event.target as HTMLInputElement
if (target.files && target.files[0]) {
uploadedFile = target.files[0]
await processRfpDocument()
}
}
async function handleDrop(event: DragEvent) {
event.preventDefault()
dragActive = false
if (event.dataTransfer?.files && event.dataTransfer.files[0]) {
uploadedFile = event.dataTransfer.files[0]
await processRfpDocument()
}
}
async function processRfpDocument() {
if (!uploadedFile) return
isProcessing = true
try {
// Upload to backend for real content extraction
const formData = new FormData()
formData.append('file', uploadedFile)
const response = await fetch('/api/upload-current-rfp', {
method: 'POST',
body: formData
})
if (!response.ok) {
throw new Error(`Upload failed: ${response.statusText}`)
}
const result = await response.json()
if (result.success) {
documentInfo = result.document
extractedContent = result.content || ''
// Create metadata for app store
const rfpMetadata: CurrentRFPDocument = {
id: result.document.id,
name: result.document.name,
size: uploadedFile.size,
uploadDate: result.document.uploadDate,
fileType: result.document.fileType,
pageCount: 1, // Will be updated by backend if available
quality: extractedContent.length > 1000 ? 'Good' : 'Fair',
keyTopics: ['Document Content'],
wordCount: extractedContent.split(' ').length,
rfpName: rfpName.trim() || undefined,
bidNumber: bidNumber.trim() || undefined
}
// Store metadata in app store
currentRfp.set(rfpMetadata)
console.log('✅ Current RFP uploaded and processed successfully')
} else {
throw new Error('Upload failed')
}
} catch (error) {
console.error('Error processing RFP:', error)
alert('Failed to process RFP document: ' + error.message)
} finally {
isProcessing = false
}
}
function proceedToQuestions() {
nextStep()
}
function removeCurrentRfp() {
uploadedFile = null
currentRfp.set(null)
extractedContent = ''
documentInfo = null
showFullContent = false
}
</script>
<div class="space-y-6">
<div class="bg-blue-500/10 border border-blue-500/20 rounded-lg p-6">
<h3 class="text-lg font-semibold text-blue-300 mb-2">Upload Current RFP</h3>
<p class="text-blue-300/80">Upload the RFP document you are responding to. We'll extract questions automatically (metadata only - content not stored).</p>
</div>
<!-- RFP Metadata Fields -->
<div class="bg-gray-800 rounded-lg border border-gray-600 p-6">
<h4 class="text-lg font-semibold text-white mb-4">RFP Information</h4>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label for="rfpName" class="block text-sm font-medium text-gray-300 mb-2">
RFP Name / Title
</label>
<input
id="rfpName"
type="text"
bind:value={rfpName}
placeholder="e.g., IT Staff Augmentation Services"
class="w-full bg-gray-700 border border-gray-600 rounded-lg px-4 py-2 text-white placeholder-gray-400 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none transition-colors"
/>
</div>
<div>
<label for="bidNumber" class="block text-sm font-medium text-gray-300 mb-2">
Bid Number / RFP ID
</label>
<input
id="bidNumber"
type="text"
bind:value={bidNumber}
placeholder="e.g., 269EMCPS-25-005"
class="w-full bg-gray-700 border border-gray-600 rounded-lg px-4 py-2 text-white placeholder-gray-400 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none transition-colors"
/>
</div>
</div>
<p class="text-sm text-gray-400 mt-2">This information will be included in the cover letter of your final document.</p>
</div>
<!-- Upload Area -->
{#if !uploadedFile}
<div
class="bg-gray-800 rounded-lg border-2 border-dashed border-gray-600 hover:border-blue-500/50 transition-colors"
class:border-blue-500={dragActive}
on:dragover|preventDefault={() => dragActive = true}
on:dragleave|preventDefault={() => dragActive = false}
on:drop={handleDrop}
>
<div class="relative p-12 text-center">
<input
type="file"
accept=".pdf,.docx,.doc,.txt"
on:change={handleFileUpload}
class="absolute inset-0 opacity-0 cursor-pointer"
/>
<div class="flex flex-col items-center space-y-4">
<div class="w-20 h-20 bg-gradient-to-r from-blue-500 to-blue-600 rounded-xl flex items-center justify-center text-white">
<svg class="w-8 h-8" fill="currentColor" viewBox="0 0 20 20">
<path d="M9 2a1 1 0 000 2h2a1 1 0 100-2H9z"></path>
<path fill-rule="evenodd" d="M4 5a2 2 0 012-2v1a1 1 0 001 1h6a1 1 0 001-1V3a2 2 0 012 2v6.5a1.5 1.5 0 01-1.5 1.5h-7A1.5 1.5 0 016 11.5V5z" clip-rule="evenodd"></path>
</svg>
</div>
<div>
<h3 class="text-xl font-semibold text-gray-200 mb-2">Upload Current RFP Document</h3>
<p class="text-gray-300 mb-2">Click to browse or drag and drop the RFP file</p>
<p class="text-sm text-gray-400">Supports PDF, DOCX, DOC, and TXT files</p>
</div>
</div>
</div>
</div>
{:else}
<!-- Current RFP Display -->
<div class="bg-gray-800 rounded-lg border border-gray-600">
<div class="bg-gray-700 p-4">
<div class="flex items-center justify-between">
<div>
<h3 class="text-lg font-semibold text-white">Current RFP Document</h3>
<p class="text-gray-300 text-sm">Metadata extracted (content not stored)</p>
</div>
<button
on:click={removeCurrentRfp}
class="bg-red-500/20 hover:bg-red-500/30 text-red-400 px-4 py-2 rounded-lg font-medium transition-colors"
>
Remove
</button>
</div>
</div>
<div class="p-4">
{#if $currentRfp}
<div class="border border-gray-600 bg-gray-700/30 rounded-lg p-4">
<div class="flex items-start justify-between">
<div class="flex-1">
<div class="flex items-center space-x-3 mb-3">
<div class="w-8 h-8 bg-blue-500/20 rounded flex items-center justify-center text-blue-300">
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
<path d="M9 2a1 1 0 000 2h2a1 1 0 100-2H9z"></path>
<path fill-rule="evenodd" d="M4 5a2 2 0 012-2v1a1 1 0 001 1h6a1 1 0 001-1V3a2 2 0 012 2v6.5a1.5 1.5 0 01-1.5 1.5h-7A1.5 1.5 0 016 11.5V5z" clip-rule="evenodd"></path>
</svg>
</div>
<div>
<h4 class="font-medium text-gray-200">{$currentRfp.name}</h4>
<p class="text-sm text-gray-400">
Size: {($currentRfp.size / 1024 / 1024).toFixed(1)} MB •
{$currentRfp.pageCount} pages (estimated) •
{$currentRfp.quality} quality
</p>
<p class="text-xs text-blue-400 mt-1">
📊 Metadata Only - Content not stored for privacy
</p>
</div>
</div>
</div>
</div>
</div>
{:else}
<div class="border border-gray-600 bg-gray-700/30 rounded-lg p-4">
<p class="text-gray-400 text-center">No current RFP uploaded</p>
</div>
{/if}
<!-- Processing and Content Section -->
{#if isProcessing}
<div class="bg-blue-500/10 border border-blue-500/20 rounded-lg p-3 mt-4">
<div class="flex items-center space-x-3">
<div class="animate-spin w-5 h-5 border-2 border-blue-500 border-t-transparent rounded-full"></div>
<span class="text-blue-300">Processing document and extracting content...</span>
</div>
</div>
{:else if extractedContent}
<div class="bg-green-500/10 border border-green-500/20 rounded-lg p-4 mt-4">
<h5 class="font-medium text-green-300 mb-2">✓ Document Processed Successfully</h5>
<p class="text-green-200 text-sm mb-3">Content extracted: {extractedContent.length.toLocaleString()} characters from {documentInfo?.name || 'PDF document'}</p>
<div class="bg-gray-800 rounded p-3 max-h-96 overflow-y-auto">
<pre class="text-xs text-gray-300 whitespace-pre-wrap">{showFullContent ? extractedContent : extractedContent.substring(0, 5000)}{!showFullContent && extractedContent.length > 5000 ? '...' : ''}</pre>
</div>
{#if extractedContent.length > 5000}
<div class="mt-2 flex justify-center">
<button
on:click={() => showFullContent = !showFullContent}
class="text-blue-400 hover:text-blue-300 text-sm underline"
>
{showFullContent ? 'Show Preview Only' : `Show Full Content (${Math.round(extractedContent.length / 1000)}k characters)`}
</button>
</div>
{/if}
</div>
{/if}
{#if extractedContent}
<div class="mt-4 flex justify-end">
<button
on:click={proceedToQuestions}
class="bg-blue-600 hover:bg-blue-700 text-white px-6 py-2 rounded-lg font-medium transition-colors"
>
Continue to Questions →
</button>
</div>
{/if}
</div>
</div>
{/if}
<!-- Tips Section -->
<div class="bg-gray-700/50 rounded-lg p-4 border border-gray-600">
<h3 class="text-base font-medium text-gray-200 mb-3">Upload Tips</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm text-gray-300">
<div>
<h4 class="font-medium mb-2 text-gray-200">Best Results:</h4>
<ul class="space-y-1">
<li>• Upload the complete RFP document</li>
<li>• Ensure questions are clearly formatted</li>
<li>• Include all sections and requirements</li>
<li>• Use high-quality, readable documents</li>
</ul>
</div>
<div>
<h4 class="font-medium mb-2 text-gray-200">What We Extract:</h4>
<ul class="space-y-1">
<li>• Individual questions and requirements</li>
<li>• Section headers and structure</li>
<li>• Evaluation criteria</li>
<li>• Submission requirements</li>
</ul>
</div>
</div>
</div>
</div>