import React, { useState, useEffect } from 'react'; import { createPortal } from 'react-dom'; import { motion, AnimatePresence } from 'framer-motion'; import { X, Search, Sparkles, Building, ChevronRight, Loader2, CheckCircle, Cpu, Tractor, HardHat, Info } from 'lucide-react'; import { useNavigate } from 'react-router-dom'; import { useAuth } from '@clerk/clerk-react'; import { createProject, lookupCompany, matchProgram } from '../../api/client'; import toast from 'react-hot-toast'; import { OnboardingTour } from '../onboarding/OnboardingTour'; import { wizardStep2TourSteps, wizardTourSteps } from '../onboarding/onboardingTours'; import { hasSeenTour, markTourSeen } from '../../utils/onboardingStorage'; import StrategyCascadePanel from '../company/StrategyCascadePanel'; interface Props { onClose: () => void; } const WizardModal: React.FC = ({ onClose }) => { const navigate = useNavigate(); const { userId } = useAuth(); const [step, setStep] = useState(1); const [nip, setNip] = useState(''); const [desc, setDesc] = useState(''); const [isSearching, setIsSearching] = useState(false); const [companyFound, setCompanyFound] = useState(false); const [analyzing, setAnalyzing] = useState(false); const [analysisLogs, setAnalysisLogs] = useState([]); const [selectedProgram, setSelectedProgram] = useState(1); const [expandedProgram, setExpandedProgram] = useState(null); const [companyDetails, setCompanyDetails] = useState<{ name: string; status: string; nip?: string; sme_status?: string; sme_confidence?: number; sme_related_entities?: number; regon?: string; krs?: string; pkd?: string[]; sources_used?: string[]; registry_score?: number; gaps?: string[]; voivodeship?: string; } | null>(null); const [recommendedPrograms, setRecommendedPrograms] = useState([]); const [manualEntry, setManualEntry] = useState(false); const [clarifyingQuestions, setClarifyingQuestions] = useState([]); const [clarificationAnswers, setClarificationAnswers] = useState([]); const [clarificationPanelOpen, setClarificationPanelOpen] = useState(false); const [runTour, setRunTour] = useState(false); const [runStep2Tour, setRunStep2Tour] = useState(false); const [selectedStrategyPath, setSelectedStrategyPath] = useState(null); useEffect(() => { if (step === 1 && !hasSeenTour('wizard')) { const timer = window.setTimeout(() => setRunTour(true), 600); return () => window.clearTimeout(timer); } if (step === 2 && !hasSeenTour('wizard')) { const timer = window.setTimeout(() => setRunStep2Tour(true), 400); return () => window.clearTimeout(timer); } return undefined; }, [step]); const handleSearchCompany = async () => { const cleanedNip = nip.replace(/\D/g, ''); if (cleanedNip.length !== 10) { toast.error("NIP musi składać się dokładnie z 10 cyfr."); return; } setIsSearching(true); try { const data = await lookupCompany(cleanedNip); setCompanyDetails({ name: data.name, status: data.status, nip: cleanedNip, sme_status: data.sme_status, sme_confidence: data.sme_confidence, sme_related_entities: data.sme_related_entities, regon: data.regon, krs: data.krs, pkd: data.pkd, sources_used: data.sources_used, registry_score: data.registry_score, gaps: data.gaps, voivodeship: data.voivodeship, }); setCompanyFound(true); setManualEntry(false); } catch(e: any) { const errMsg = e.response?.data?.detail || "Nie znaleziono w bazie GUS. Możesz kontynuować, wpisując nazwę ręcznie."; toast.error(errMsg); setCompanyFound(false); setManualEntry(true); setCompanyDetails({ name: '', status: 'Dane wprowadzone ręcznie', nip: nip }); } finally { setIsSearching(false); } }; const handleStartAnalysis = async ( userAnswers?: { question: string; answer: string }[], strategyPathOverride?: string | null, opts?: { preserveClarifications?: boolean }, ) => { if (!desc && !userAnswers?.length) return; setStep(3); setAnalyzing(true); const isPathRerank = Boolean(opts?.preserveClarifications); setAnalysisLogs(prev => [ ...prev, userAnswers?.length ? 'Aktualizacja analizy z nowymi danymi...' : isPathRerank ? 'Przeliczanie matchingu dla wybranej ścieżki strategii...' : 'Inicjalizowanie silnika RAG...', ]); const additionalContext = userAnswers ?.map((qa) => (qa.answer ? `Pytanie: ${qa.question}\nOdpowiedź: ${qa.answer}` : '')) .filter(Boolean) .join('\n\n'); const finalDesc = additionalContext ? `${desc}\n\n[DOPRECYZOWANIE]:\n${additionalContext}` : desc; if (additionalContext) setDesc(finalDesc); const pathForMatch = strategyPathOverride !== undefined ? strategyPathOverride : selectedStrategyPath; try { const data = await matchProgram(finalDesc, nip, userAnswers, { strategy_path: pathForMatch || undefined, }); setRecommendedPrograms(data.programs || []); if (data.programs && data.programs.length > 0) setSelectedProgram(data.programs[0].id); if (!opts?.preserveClarifications) { const nextQuestions = data.clarifying_questions || []; setClarifyingQuestions(nextQuestions); setClarificationAnswers(new Array(nextQuestions.length).fill('')); setClarificationPanelOpen(nextQuestions.length > 0); } // Prefer path returned by match (normalized) when present if (typeof data.strategy_path === 'string' && data.strategy_path) { setSelectedStrategyPath(data.strategy_path); } setAnalysisLogs(prev => [ ...prev, pathForMatch ? `Zakończono generowanie raportu dopasowania (ścieżka: ${pathForMatch}).` : 'Zakończono generowanie raportu dopasowania.', ]); } catch(e) { toast.error("Wystąpił problem ze zgłaszaniem do modelu AI"); } finally { setAnalyzing(false); } }; const renderStepContent = () => { switch(step) { case 1: return (

Podłącz profil firmy

Multi-rejestr: GUS BIR, CEIDG, KRS, Biała Lista MF, SUDOP (pomoc publiczna), Bizraport — podstawa wiarygodnego matchingu.

setNip(e.target.value)} style={{ width: '100%', padding: '1rem 1rem 1rem 3rem', background: 'rgba(255,255,255,0.03)', border: '1px solid rgba(59, 130, 246, 0.4)', borderRadius: '12px', color: '#fff', fontSize: '1.1rem', outline: 'none' }} onKeyDown={e => e.key === 'Enter' && handleSearchCompany()} />
{import.meta.env.DEV && ( )} {!manualEntry && ( { setManualEntry(true); setCompanyDetails({ name: '', status: 'Dane wprowadzone ręcznie', nip: nip }); }}> Wprowadź dane ręcznie )}
{companyFound && !manualEntry && companyDetails && (
Zweryfikowano w rejestrach
NIP: {nip.replace(/\D/g, '')}
{companyDetails.name}
{companyDetails.status}
{(companyDetails.regon || companyDetails.krs) && (
{companyDetails.regon ? `REGON: ${companyDetails.regon}` : ''} {companyDetails.regon && companyDetails.krs ? ' · ' : ''} {companyDetails.krs ? `KRS: ${companyDetails.krs}` : ''}
)} {companyDetails.pkd && companyDetails.pkd.length > 0 && (
PKD: {companyDetails.pkd.slice(0, 5).join(', ')}
)} {companyDetails.sources_used && companyDetails.sources_used.length > 0 && (
Źródła: {companyDetails.sources_used.join(', ')} {typeof companyDetails.registry_score === 'number' ? ` · wiarygodność rejestrów ${(companyDetails.registry_score * 100).toFixed(0)}%` : ''}
)}
{companyDetails.sme_status && (
Weryfikacja MŚP (GraphRAG)
Wyliczony status: {companyDetails.sme_status.toUpperCase()}
Wykryte powiązania kapitałowe/osobowe: {companyDetails.sme_related_entities || 0} podmiotów
)} {companyDetails.gaps && companyDetails.gaps.length > 0 && (
Luki do uzupełnienia: {companyDetails.gaps.slice(0, 3).join(' · ')}
)}
)} {manualEntry && ( setCompanyDetails({ name: e.target.value, status: 'Dane wpisane ręcznie', nip: nip })} placeholder="Wpisz pełną nazwę przedsiębiorstwa..." style={{ width: '100%', padding: '1rem', background: 'rgba(255,255,255,0.03)', border: '1px solid rgba(245, 158, 11, 0.4)', borderRadius: '12px', color: '#fff', fontSize: '1.1rem', outline: 'none' }} /> )}
); case 2: return (

Opisz planowaną inwestycję

Napisz swoimi słowami, co zamierzasz zrobić. Sztuczna Inteligencja przejdzie przez tekst wyciągając kryteria kluczowe dla dotacji.