Spaces:
Sleeping
Sleeping
| <script lang="ts"> | |
| import { rfpData, nextStep, isLoading } from '../stores/appStore' | |
| import { parseDocument } from '../utils/documentParser' | |
| let previousRFPFiles: FileList | null = null | |
| let newQuestionsFile: FileList | null = null | |
| let dragOver = false | |
| async function handlePreviousRFPUpload() { | |
| if (!previousRFPFiles || previousRFPFiles.length === 0) return | |
| isLoading.set(true) | |
| try { | |
| const files = Array.from(previousRFPFiles) | |
| rfpData.update(data => ({ ...data, previousRFPs: files })) | |
| // Parse documents to extract previous answers | |
| const extractedData = [] | |
| for (const file of files) { | |
| const parsed = await parseDocument(file) | |
| extractedData.push(parsed) | |
| } | |
| rfpData.update(data => ({ ...data, extractedData })) | |
| } catch (error) { | |
| console.error('Error parsing previous RFPs:', error) | |
| } finally { | |
| isLoading.set(false) | |
| } | |
| } | |
| async function handleNewQuestionsUpload() { | |
| if (!newQuestionsFile || newQuestionsFile.length === 0) return | |
| isLoading.set(true) | |
| try { | |
| const file = newQuestionsFile[0] | |
| rfpData.update(data => ({ ...data, newQuestions: file })) | |
| // Parse questions from the uploaded file | |
| const parsed = await parseDocument(file) | |
| const questions = extractQuestions(parsed.content) | |
| rfpData.update(data => ({ ...data, questions })) | |
| } catch (error) { | |
| console.error('Error parsing questions:', error) | |
| } finally { | |
| isLoading.set(false) | |
| } | |
| } | |
| function extractQuestions(content: string): string[] { | |
| // Simple question extraction - split by numbers or question marks | |
| const lines = content.split('\n').filter(line => line.trim().length > 0) | |
| return lines.filter(line => | |
| /^\d+\./.test(line.trim()) || | |
| line.includes('?') || | |
| /^Q\d+/.test(line.trim()) | |
| ) | |
| } | |
| function handleDrop(event: DragEvent, type: 'previous' | 'questions') { | |
| event.preventDefault() | |
| dragOver = false | |
| const files = event.dataTransfer?.files | |
| if (files) { | |
| if (type === 'previous') { | |
| previousRFPFiles = files | |
| handlePreviousRFPUpload() | |
| } else { | |
| newQuestionsFile = files | |
| handleNewQuestionsUpload() | |
| } | |
| } | |
| } | |
| function handleDragOver(event: DragEvent) { | |
| event.preventDefault() | |
| dragOver = true | |
| } | |
| function handleDragLeave() { | |
| dragOver = false | |
| } | |
| function canProceed(): boolean { | |
| const data = $rfpData | |
| return data.previousRFPs.length > 0 && data.newQuestions !== null && data.questions.length > 0 | |
| } | |
| </script> | |
| <script lang="ts"> | |
| import { rfpData, nextStep, isLoading } from '../stores/appStore' | |
| import { parseDocument } from '../utils/documentParser' | |
| let previousRFPFiles: FileList | null = null | |
| let newQuestionsFile: FileList | null = null | |
| let dragOver = false | |
| async function handlePreviousRFPUpload() { | |
| if (!previousRFPFiles || previousRFPFiles.length === 0) return | |
| isLoading.set(true) | |
| try { | |
| const files = Array.from(previousRFPFiles) | |
| rfpData.update(data => ({ ...data, previousRFPs: files })) | |
| // Parse documents to extract previous answers | |
| const extractedData = [] | |
| for (const file of files) { | |
| const parsed = await parseDocument(file) | |
| extractedData.push(parsed) | |
| } | |
| rfpData.update(data => ({ ...data, extractedData })) | |
| } catch (error) { | |
| console.error('Error parsing previous RFPs:', error) | |
| } finally { | |
| isLoading.set(false) | |
| } | |
| } | |
| async function handleNewQuestionsUpload() { | |
| if (!newQuestionsFile || newQuestionsFile.length === 0) return | |
| isLoading.set(true) | |
| try { | |
| const file = newQuestionsFile[0] | |
| rfpData.update(data => ({ ...data, newQuestions: file })) | |
| // Parse questions from the uploaded file | |
| const parsed = await parseDocument(file) | |
| const questions = extractQuestions(parsed.content) | |
| rfpData.update(data => ({ ...data, questions })) | |
| } catch (error) { | |
| console.error('Error parsing questions:', error) | |
| } finally { | |
| isLoading.set(false) | |
| } | |
| } | |
| function extractQuestions(content: string): string[] { | |
| // Simple question extraction - split by numbers or question marks | |
| const lines = content.split('\n').filter(line => line.trim().length > 0) | |
| return lines.filter(line => | |
| /^\d+\./.test(line.trim()) || | |
| line.includes('?') || | |
| /^Q\d+/.test(line.trim()) | |
| ) | |
| } | |
| function handleDrop(event: DragEvent, type: 'previous' | 'questions') { | |
| event.preventDefault() | |
| dragOver = false | |
| const files = event.dataTransfer?.files | |
| if (files) { | |
| if (type === 'previous') { | |
| previousRFPFiles = files | |
| handlePreviousRFPUpload() | |
| } else { | |
| newQuestionsFile = files | |
| handleNewQuestionsUpload() | |
| } | |
| } | |
| } | |
| function handleDragOver(event: DragEvent) { | |
| event.preventDefault() | |
| dragOver = true | |
| } | |
| function handleDragLeave() { | |
| dragOver = false | |
| } | |
| function canProceed(): boolean { | |
| const data = $rfpData | |
| return data.previousRFPs.length > 0 && data.newQuestions !== null && data.questions.length > 0 | |
| } | |
| </script> | |
| <div class="h-full flex flex-col bg-white dark:bg-slate-800"> | |
| <!-- Hero Section --> | |
| <div class="bg-gradient-to-r from-blue-600 via-indigo-600 to-purple-600 text-white p-8"> | |
| <div class="max-w-4xl mx-auto text-center"> | |
| <h1 class="text-4xl font-bold mb-4">Upload Your RFP Documents</h1> | |
| <p class="text-xl text-blue-100 mb-6"> | |
| Start by uploading your previous RFP responses and new questions to leverage AI-powered answer generation | |
| </p> | |
| <div class="flex justify-center space-x-8 text-sm"> | |
| <div class="flex items-center space-x-2"> | |
| <div class="w-2 h-2 bg-blue-200 rounded-full"></div> | |
| <span>PDF & Word Support</span> | |
| </div> | |
| <div class="flex items-center space-x-2"> | |
| <div class="w-2 h-2 bg-blue-200 rounded-full"></div> | |
| <span>Automatic Processing</span> | |
| </div> | |
| <div class="flex items-center space-x-2"> | |
| <div class="w-2 h-2 bg-blue-200 rounded-full"></div> | |
| <span>Citation Tracking</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Main Content --> | |
| <div class="flex-1 p-8 overflow-auto"> | |
| <div class="max-w-7xl mx-auto"> | |
| <div class="grid grid-cols-1 lg:grid-cols-2 gap-8"> | |
| <!-- Previous RFPs Upload --> | |
| <div class="space-y-6"> | |
| <div class="bg-gradient-to-br from-blue-50 to-indigo-50 dark:from-blue-900/20 dark:to-indigo-900/20 rounded-2xl p-6 border border-blue-200 dark:border-blue-800"> | |
| <div class="flex items-center space-x-3 mb-4"> | |
| <div class="w-12 h-12 bg-blue-600 rounded-xl flex items-center justify-center"> | |
| <span class="text-2xl">📁</span> | |
| </div> | |
| <div> | |
| <h3 class="text-xl font-bold text-slate-900 dark:text-white">Previous RFP Responses</h3> | |
| <p class="text-blue-600 dark:text-blue-400 text-sm font-medium">Reference Documents</p> | |
| </div> | |
| </div> | |
| <p class="text-slate-600 dark:text-slate-300 mb-6 leading-relaxed"> | |
| Upload your historical RFP responses (PDF or Word documents) to use as intelligent reference material for generating new answers. | |
| </p> | |
| <div | |
| class="border-2 border-dashed rounded-xl p-8 text-center transition-all duration-300 { | |
| dragOver | |
| ? 'border-blue-400 bg-blue-50 dark:bg-blue-900/20 scale-105' | |
| : 'border-slate-300 dark:border-slate-600 hover:border-blue-400 hover:bg-slate-50 dark:hover:bg-slate-700' | |
| }" | |
| role="button" | |
| tabindex="0" | |
| on:drop={(e) => handleDrop(e, 'previous')} | |
| on:dragover={handleDragOver} | |
| on:dragleave={handleDragLeave} | |
| > | |
| <div class="space-y-4"> | |
| <div class="w-16 h-16 mx-auto bg-slate-100 dark:bg-slate-700 rounded-xl flex items-center justify-center"> | |
| <svg class="w-8 h-8 text-slate-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" /> | |
| </svg> | |
| </div> | |
| <div> | |
| <p class="text-lg font-medium text-slate-900 dark:text-white mb-2"> | |
| Drop files here or browse to upload | |
| </p> | |
| <label class="cursor-pointer"> | |
| <span class="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors font-medium"> | |
| <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 6v6m0 0v6m0-6h6m-6 0H6" /> | |
| </svg> | |
| Select Files | |
| </span> | |
| <input | |
| type="file" | |
| class="hidden" | |
| multiple | |
| accept=".pdf,.doc,.docx" | |
| bind:files={previousRFPFiles} | |
| on:change={handlePreviousRFPUpload} | |
| /> | |
| </label> | |
| <p class="text-sm text-slate-500 dark:text-slate-400 mt-2"> | |
| PDF, DOC, DOCX • Multiple files supported | |
| </p> | |
| </div> | |
| </div> | |
| </div> | |
| {#if $rfpData.previousRFPs.length > 0} | |
| <div class="mt-6 space-y-3"> | |
| <h4 class="font-semibold text-slate-900 dark:text-white flex items-center"> | |
| <svg class="w-4 h-4 mr-2 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" /> | |
| </svg> | |
| Uploaded Files ({$rfpData.previousRFPs.length}) | |
| </h4> | |
| <div class="space-y-2 max-h-32 overflow-y-auto"> | |
| {#each $rfpData.previousRFPs as file} | |
| <div class="flex items-center justify-between p-3 bg-green-50 dark:bg-green-900/20 rounded-lg border border-green-200 dark:border-green-800"> | |
| <div class="flex items-center space-x-3"> | |
| <div class="w-8 h-8 bg-green-100 dark:bg-green-800 rounded-lg flex items-center justify-center"> | |
| <svg class="w-4 h-4 text-green-600" 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> | |
| <div class="font-medium text-green-800 dark:text-green-200 text-sm">{file.name}</div> | |
| <div class="text-green-600 dark:text-green-400 text-xs"> | |
| {(file.size / 1024 / 1024).toFixed(2)} MB | |
| </div> | |
| </div> | |
| </div> | |
| <div class="text-green-600"> | |
| <svg class="w-5 h-5" 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> | |
| </div> | |
| {/each} | |
| </div> | |
| </div> | |
| {/if} | |
| </div> | |
| </div> | |
| <!-- New Questions Upload --> | |
| <div class="space-y-6"> | |
| <div class="bg-gradient-to-br from-indigo-50 to-purple-50 dark:from-indigo-900/20 dark:to-purple-900/20 rounded-2xl p-6 border border-indigo-200 dark:border-indigo-800"> | |
| <div class="flex items-center space-x-3 mb-4"> | |
| <div class="w-12 h-12 bg-indigo-600 rounded-xl flex items-center justify-center"> | |
| <span class="text-2xl">📋</span> | |
| </div> | |
| <div> | |
| <h3 class="text-xl font-bold text-slate-900 dark:text-white">New RFP Questions</h3> | |
| <p class="text-indigo-600 dark:text-indigo-400 text-sm font-medium">Questions to Answer</p> | |
| </div> | |
| </div> | |
| <p class="text-slate-600 dark:text-slate-300 mb-6 leading-relaxed"> | |
| Upload the new RFP document containing questions that need to be answered using AI-powered analysis. | |
| </p> | |
| <div | |
| class="border-2 border-dashed rounded-xl p-8 text-center transition-all duration-300 { | |
| dragOver | |
| ? 'border-indigo-400 bg-indigo-50 dark:bg-indigo-900/20 scale-105' | |
| : 'border-slate-300 dark:border-slate-600 hover:border-indigo-400 hover:bg-slate-50 dark:hover:bg-slate-700' | |
| }" | |
| role="button" | |
| tabindex="0" | |
| on:drop={(e) => handleDrop(e, 'questions')} | |
| on:dragover={handleDragOver} | |
| on:dragleave={handleDragLeave} | |
| > | |
| <div class="space-y-4"> | |
| <div class="w-16 h-16 mx-auto bg-slate-100 dark:bg-slate-700 rounded-xl flex items-center justify-center"> | |
| <svg class="w-8 h-8 text-slate-500" 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> | |
| <p class="text-lg font-medium text-slate-900 dark:text-white mb-2"> | |
| Drop RFP document here | |
| </p> | |
| <label class="cursor-pointer"> | |
| <span class="inline-flex items-center px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition-colors font-medium"> | |
| <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 6v6m0 0v6m0-6h6m-6 0H6" /> | |
| </svg> | |
| Browse File | |
| </span> | |
| <input | |
| type="file" | |
| class="hidden" | |
| accept=".pdf,.doc,.docx" | |
| bind:files={newQuestionsFile} | |
| on:change={handleNewQuestionsUpload} | |
| /> | |
| </label> | |
| <p class="text-sm text-slate-500 dark:text-slate-400 mt-2"> | |
| PDF, DOC, DOCX • Single file | |
| </p> | |
| </div> | |
| </div> | |
| </div> | |
| {#if $rfpData.newQuestions} | |
| <div class="mt-6"> | |
| <div class="flex items-center justify-between p-4 bg-green-50 dark:bg-green-900/20 rounded-lg border border-green-200 dark:border-green-800"> | |
| <div class="flex items-center space-x-3"> | |
| <div class="w-8 h-8 bg-green-100 dark:bg-green-800 rounded-lg flex items-center justify-center"> | |
| <svg class="w-4 h-4 text-green-600" 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> | |
| <div class="font-medium text-green-800 dark:text-green-200 text-sm">{$rfpData.newQuestions.name}</div> | |
| <div class="text-green-600 dark:text-green-400 text-xs"> | |
| {($rfpData.newQuestions.size / 1024 / 1024).toFixed(2)} MB | |
| </div> | |
| </div> | |
| </div> | |
| <div class="text-green-600"> | |
| <svg class="w-5 h-5" 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> | |
| </div> | |
| {#if $rfpData.questions.length > 0} | |
| <div class="mt-4 p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg border border-blue-200 dark:border-blue-800"> | |
| <h5 class="font-semibold text-blue-900 dark:text-blue-100 mb-3 flex items-center"> | |
| <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="M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4" /> | |
| </svg> | |
| Detected Questions ({$rfpData.questions.length}) | |
| </h5> | |
| <div class="space-y-2 max-h-32 overflow-y-auto"> | |
| {#each $rfpData.questions.slice(0, 3) as question} | |
| <div class="text-sm text-blue-800 dark:text-blue-200 p-2 bg-white dark:bg-slate-800 rounded border border-blue-100 dark:border-blue-800"> | |
| {question} | |
| </div> | |
| {/each} | |
| {#if $rfpData.questions.length > 3} | |
| <div class="text-sm text-blue-600 dark:text-blue-400 italic text-center p-2"> | |
| +{$rfpData.questions.length - 3} more questions detected... | |
| </div> | |
| {/if} | |
| </div> | |
| </div> | |
| {/if} | |
| </div> | |
| {/if} | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Loading State --> | |
| {#if $isLoading} | |
| <div class="absolute inset-0 bg-white/80 dark:bg-slate-800/80 backdrop-blur-sm flex items-center justify-center z-50"> | |
| <div class="bg-white dark:bg-slate-800 rounded-2xl shadow-2xl p-8 max-w-md w-full mx-4"> | |
| <div class="text-center"> | |
| <div class="inline-flex items-center justify-center w-16 h-16 bg-blue-100 dark:bg-blue-900/20 rounded-full mb-4"> | |
| <div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div> | |
| </div> | |
| <h3 class="text-lg font-semibold text-slate-900 dark:text-white mb-2">Processing Documents</h3> | |
| <p class="text-slate-600 dark:text-slate-300 mb-4">Extracting content and analyzing structure...</p> | |
| <div class="w-full bg-slate-200 dark:bg-slate-700 rounded-full h-2"> | |
| <div class="bg-gradient-to-r from-blue-500 to-indigo-500 h-2 rounded-full animate-pulse" style="width: 60%"></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| {/if} | |
| <!-- Action Bar --> | |
| <div class="border-t border-slate-200 dark:border-slate-700 bg-slate-50 dark:bg-slate-800 p-6"> | |
| <div class="max-w-7xl mx-auto flex items-center justify-between"> | |
| <div class="flex items-center space-x-4"> | |
| <div class="flex items-center space-x-2 text-sm text-slate-600 dark:text-slate-400"> | |
| <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> | |
| </svg> | |
| {#if canProceed()} | |
| <span class="text-green-600 dark:text-green-400 font-medium">Ready to proceed</span> | |
| {:else} | |
| <span>Upload both previous RFPs and new questions to continue</span> | |
| {/if} | |
| </div> | |
| </div> | |
| <button | |
| class="inline-flex items-center px-6 py-3 bg-gradient-to-r from-blue-600 to-indigo-600 text-white rounded-xl font-semibold hover:from-blue-700 hover:to-indigo-700 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 shadow-lg hover:shadow-xl" | |
| disabled={!canProceed() || $isLoading} | |
| on:click={nextStep} | |
| > | |
| Continue to Questions | |
| <svg class="w-5 h-5 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7l5 5m0 0l-5 5m5-5H6" /> | |
| </svg> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| {#if $rfpData.previousRFPs.length > 0} | |
| <div class="mt-4"> | |
| <h4 class="font-medium text-gray-900 dark:text-white mb-2">Uploaded Files:</h4> | |
| <div class="space-y-2"> | |
| {#each $rfpData.previousRFPs as file} | |
| <div class="flex items-center p-3 bg-green-50 dark:bg-green-900/20 rounded-lg border border-green-200 dark:border-green-800"> | |
| <svg class="w-5 h-5 text-green-600 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" /> | |
| </svg> | |
| <span class="text-green-800 dark:text-green-200 font-medium">{file.name}</span> | |
| <span class="text-green-600 dark:text-green-400 text-sm ml-auto"> | |
| {(file.size / 1024 / 1024).toFixed(2)} MB | |
| </span> | |
| </div> | |
| {/each} | |
| </div> | |
| </div> | |
| {/if} | |
| </div> | |
| <!-- New Questions Upload --> | |
| <div class="mb-8"> | |
| <h3 class="text-xl font-semibold text-gray-900 dark:text-white mb-4"> | |
| 2. Upload New RFP Questions | |
| </h3> | |
| <p class="text-gray-600 dark:text-gray-300 mb-4"> | |
| Upload the new RFP document containing questions you need to answer. | |
| </p> | |
| <div | |
| class="border-2 border-dashed rounded-lg p-8 text-center transition-colors { | |
| dragOver | |
| ? 'border-blue-400 bg-blue-50 dark:bg-blue-900/20' | |
| : 'border-gray-300 dark:border-gray-600 hover:border-gray-400 dark:hover:border-gray-500' | |
| }" | |
| role="button" | |
| tabindex="0" | |
| on:drop={(e) => handleDrop(e, 'questions')} | |
| on:dragover={handleDragOver} | |
| on:dragleave={handleDragLeave} | |
| > | |
| <svg class="mx-auto h-12 w-12 text-gray-400 dark:text-gray-500 mb-4" stroke="currentColor" fill="none" viewBox="0 0 48 48"> | |
| <path d="M9 12h6m6 0h6m-6 6h6m-12 6h12m-12 6h6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /> | |
| </svg> | |
| <div class="text-lg font-medium text-gray-900 dark:text-white mb-2"> | |
| Drop your RFP questions file here, or | |
| </div> | |
| <label class="cursor-pointer"> | |
| <span class="text-blue-600 hover:text-blue-500 font-medium">browse to upload</span> | |
| <input | |
| type="file" | |
| class="hidden" | |
| accept=".pdf,.doc,.docx" | |
| bind:files={newQuestionsFile} | |
| on:change={handleNewQuestionsUpload} | |
| /> | |
| </label> | |
| <p class="text-sm text-gray-500 dark:text-gray-400 mt-2"> | |
| Supports PDF, DOC, DOCX files (single file) | |
| </p> | |
| </div> | |
| {#if $rfpData.newQuestions} | |
| <div class="mt-4"> | |
| <div class="flex items-center p-3 bg-green-50 dark:bg-green-900/20 rounded-lg border border-green-200 dark:border-green-800"> | |
| <svg class="w-5 h-5 text-green-600 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" /> | |
| </svg> | |
| <span class="text-green-800 dark:text-green-200 font-medium">{$rfpData.newQuestions.name}</span> | |
| <span class="text-green-600 dark:text-green-400 text-sm ml-auto"> | |
| {($rfpData.newQuestions.size / 1024 / 1024).toFixed(2)} MB | |
| </span> | |
| </div> | |
| {#if $rfpData.questions.length > 0} | |
| <div class="mt-4 p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg border border-blue-200 dark:border-blue-800"> | |
| <h5 class="font-medium text-blue-900 dark:text-blue-100 mb-2"> | |
| Detected Questions ({$rfpData.questions.length}): | |
| </h5> | |
| <div class="max-h-32 overflow-y-auto space-y-1"> | |
| {#each $rfpData.questions.slice(0, 3) as question} | |
| <div class="text-sm text-blue-800 dark:text-blue-200 truncate"> | |
| {question} | |
| </div> | |
| {/each} | |
| {#if $rfpData.questions.length > 3} | |
| <div class="text-sm text-blue-600 dark:text-blue-400 italic"> | |
| +{$rfpData.questions.length - 3} more questions... | |
| </div> | |
| {/if} | |
| </div> | |
| </div> | |
| {/if} | |
| </div> | |
| {/if} | |
| </div> | |
| <!-- Loading State --> | |
| {#if $isLoading} | |
| <div class="flex items-center justify-center p-8"> | |
| <div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div> | |
| <span class="ml-3 text-gray-600 dark:text-gray-300">Processing documents...</span> | |
| </div> | |
| {/if} | |
| <!-- Continue Button --> | |
| <div class="flex justify-end"> | |
| <button | |
| class="px-6 py-3 bg-blue-600 text-white rounded-lg font-medium hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors" | |
| disabled={!canProceed() || $isLoading} | |
| on:click={nextStep} | |
| > | |
| Continue to Question Review | |
| <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="M9 5l7 7-7 7" /> | |
| </svg> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |