Spaces:
Sleeping
Sleeping
| <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> | |