'use client'; import React, { useState, useEffect } from 'react'; import api, { Project, CreateProjectData, RFPOpportunity, SavedOpportunity } from '../services/api'; interface ProjectModalProps { project: Project | null; onSave: (projectData: CreateProjectData, projectId?: string) => void; onClose: () => void; } type ModalStep = 'select' | 'opportunities' | 'manual'; // Unified opportunity type for display interface DisplayOpportunity { id: string; title: string; department: string; description?: string; response_deadline?: string; source?: string; url?: string; } const ProjectModal: React.FC = ({ project, onSave, onClose }) => { const [step, setStep] = useState(project ? 'manual' : 'select'); // Opportunities state - now uses database opportunities const [opportunities, setOpportunities] = useState([]); const [loadingOpps, setLoadingOpps] = useState(false); const [searchQuery, setSearchQuery] = useState(''); const [selectedOpp, setSelectedOpp] = useState(null); // Form state const [name, setName] = useState(''); const [client, setClient] = useState(''); const [dueDate, setDueDate] = useState(''); const [description, setDescription] = useState(''); useEffect(() => { if (project) { setName(project.name); setClient(project.client); setDueDate(project.due_date ? new Date(project.due_date).toISOString().split('T')[0] : ''); setDescription(project.description || ''); setStep('manual'); } else { setName(''); setClient(''); setDueDate(''); setDescription(''); setStep('select'); } }, [project]); // Fetch opportunities when entering opportunities step useEffect(() => { if (step === 'opportunities' && opportunities.length === 0) { fetchOpportunities(); } }, [step]); const fetchOpportunities = async () => { setLoadingOpps(true); try { // Fetch saved opportunities from the database const result = await api.opportunities.getSaved({ limit: 100 }); // Map saved opportunities to display format const displayOpps: DisplayOpportunity[] = (result.opportunities || []).map((opp: SavedOpportunity) => ({ id: opp.id, title: opp.title, department: opp.department || 'Unknown', description: opp.description, response_deadline: opp.response_deadline, source: opp.province || opp.source_key || 'Database', url: opp.url, })); setOpportunities(displayOpps); } catch (err) { console.error('Failed to fetch opportunities:', err); setOpportunities([]); } finally { setLoadingOpps(false); } }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); const projectData: CreateProjectData = { name, client, due_date: dueDate, description, }; onSave(projectData, project?.id); }; const handleCreateFromOpportunity = () => { if (!selectedOpp) return; const projectData: CreateProjectData = { name: selectedOpp.title, client: selectedOpp.department || '', due_date: selectedOpp.response_deadline || '', description: selectedOpp.description || '', }; onSave(projectData); }; const handleAutoRFPClick = () => { setStep('opportunities'); }; const handleManualClick = () => { setStep('manual'); }; const formatDate = (dateString?: string) => { if (!dateString) return '-'; try { return new Date(dateString).toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' }); } catch { return dateString; } }; // Filter opportunities by search const filteredOpportunities = opportunities.filter(opp => { if (!searchQuery.trim()) return true; const query = searchQuery.toLowerCase(); return ( opp.title.toLowerCase().includes(query) || opp.department.toLowerCase().includes(query) || opp.description?.toLowerCase().includes(query) ); }); // Selection Step - Choose how to create workspace if (step === 'select') { return (
{/* Backdrop */}
{/* Modal */}
{/* Header */}

Create New Workspace

{/* Content */}

How would you like to start your workspace?

{/* Auto RFP Opportunities Option */} {/* Manual Entry Option */}
); } // Opportunities Selection Step if (step === 'opportunities') { return (
{/* Backdrop */}
{/* Modal */}
{/* Header */}

Select Opportunity

{/* Content */}
{/* Search */}
search setSearchQuery(e.target.value)} className="w-full pl-10 pr-4 py-2.5 rounded-lg border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-700 text-slate-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition shadow-sm placeholder-slate-400 dark:placeholder-slate-500" placeholder="Search tenders by title or buyer..." />
{/* Opportunities List */}
{loadingOpps ? (

Loading opportunities...

) : filteredOpportunities.length === 0 ? (
search_off

No opportunities found

) : ( filteredOpportunities.map((opp) => (
setSelectedOpp(opp)} className={`border rounded-lg p-4 cursor-pointer transition bg-white dark:bg-slate-700 group shadow-sm ${ selectedOpp?.id === opp.id ? 'border-blue-500 ring-2 ring-blue-500/20' : 'border-slate-200 dark:border-slate-600 hover:border-blue-400 dark:hover:border-blue-400' }`} >

{opp.title}

business {opp.department}
place {opp.source || 'Database'}
{opp.response_deadline && (
calendar_today {formatDate(opp.response_deadline)}
)}
)) )}
{/* Footer */}
); } // Manual Entry Form Step return (
{/* Backdrop */}
{/* Modal */}
{/* Header */}
{!project ? ( ) : (
)}

{project ? 'Edit Workspace' : 'Enter Opportunity Details'}

{/* Form */}
{/* Project Title */}
description setName(e.target.value)} placeholder="e.g. Website Redesign Project" className="block w-full pl-10 pr-4 py-2.5 bg-white dark:bg-slate-700 border border-slate-300 dark:border-slate-600 rounded-lg shadow-sm placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors" required />
{/* Client / Buyer */}
business setClient(e.target.value)} placeholder="e.g. Acme Corporation" className="block w-full pl-10 pr-4 py-2.5 bg-white dark:bg-slate-700 border border-slate-300 dark:border-slate-600 rounded-lg shadow-sm placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors" required />
{/* Deadline */}
calendar_today setDueDate(e.target.value)} className="block w-full pl-10 pr-4 py-2.5 bg-white dark:bg-slate-700 border border-slate-300 dark:border-slate-600 rounded-lg shadow-sm placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors" />
{/* Description / Notes */}