import React, { useState, useEffect } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { Search, Plus, Filter, FolderOpen, MoreVertical, Clock, CheckCircle, ChevronRight, Activity, Trash2, Download, Grid, List, Sparkles } from 'lucide-react'; import { useNavigate, useLocation } from 'react-router-dom'; import WizardModal from '../components/dashboard/WizardModal'; import EmptyProjectsState from '../components/dashboard/EmptyProjectsState'; import { useProjectStore } from '../store/useProjectStore'; import { deleteProject } from '../api/client'; import toast from 'react-hot-toast'; import * as XLSX from 'xlsx'; import { analytics } from '../utils/analytics'; const Projects: React.FC = () => { const navigate = useNavigate(); const location = useLocation(); const { projects, fetchProjects } = useProjectStore(); const [searchQuery, setSearchQuery] = useState(''); const [statusFilter, setStatusFilter] = useState('all'); const [showWizard, setShowWizard] = useState(false); const [currentPage, setCurrentPage] = useState(1); const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid'); const [menuOpenId, setMenuOpenId] = useState(null); const itemsPerPage = 6; useEffect(() => { fetchProjects(); }, [fetchProjects]); // Obsługa powrotu po Stripe Checkout useEffect(() => { const params = new URLSearchParams(location.search); if (params.get('upgraded') === '1' || params.get('mock_success') === '1') { const tier = params.get('tier') || 'pro'; toast.success( `🎉 Plan ${tier === 'pro' ? 'Pro' : 'Business'} aktywny! Limity zostały zwiększone.`, { duration: 6000, icon: } ); analytics.checkoutCompleted(tier); // Usuń parametry z URL bez reload window.history.replaceState({}, '', '/projects'); } }, [location.search]); // Filter logic const filteredProjects = projects.filter(p => { if (statusFilter !== 'all' && p.status !== statusFilter) return false; if (searchQuery && !p.title.toLowerCase().includes(searchQuery.toLowerCase())) return false; return true; }); const getStatusConfig = (p: any) => { let progress = 0; if (p.sections && p.sections.length > 0) { const approvedCount = p.sections.filter((s: any) => s.is_approved).length; progress = Math.round((approvedCount / p.sections.length) * 100); } else { let baseProgress = 0; if (p.external_context) { if (p.external_context.region) baseProgress += 10; if (p.external_context.company_size) baseProgress += 10; if (p.external_context.innovation_scale) baseProgress += 10; if (p.external_context.ai_matches && p.external_context.ai_matches.length > 0) baseProgress += 20; } if (p.title && p.title !== 'Nowy Projekt') baseProgress += 5; if (p.description && p.description.length > 10) baseProgress += 15; progress = Math.min(baseProgress, 99); // max 99 if no sections if (p.status === 'completed') progress = 100; } let statusToUse = p.status; if (statusToUse === 'draft') { const filledCount = p.sections?.filter((s: any) => s.content && s.content.length > 50).length || 0; const approvedCount = p.sections?.filter((s: any) => s.is_approved).length || 0; if (approvedCount > 0 || filledCount > 0 || progress > 5) statusToUse = 'in_progress'; } switch(statusToUse) { case 'draft': return { label: 'Szkic', color: 'var(--text-muted)', bg: 'rgba(255, 255, 255, 0.05)', icon: , progress }; case 'in_progress': return { label: 'W Trakcie', color: 'var(--accent-blue)', bg: 'rgba(59, 130, 246, 0.1)', icon: , progress }; case 'completed': return { label: 'Zakończony (Wygenerowany)', color: 'var(--accent-green)', bg: 'rgba(16, 185, 129, 0.1)', icon: , progress: 100 }; default: return { label: 'Nieznany', color: 'var(--text-muted)', bg: 'var(--bg-elevated)', icon: null, progress: 0 }; } }; const formatDate = (dateString: string) => { const d = new Date(dateString); return d.toLocaleDateString('pl-PL'); }; const handleDelete = async (id: string, e: React.MouseEvent) => { e.stopPropagation(); if (window.confirm("Czy na pewno chcesz usunąć projekt? Ta operacja jest nieodwracalna.")) { try { await deleteProject(id); toast.success("Projekt usunięty"); fetchProjects(); } catch (error) { toast.error("Błąd podczas usuwania projektu"); } } setMenuOpenId(null); }; const handleExport = () => { const dataToExport = filteredProjects.map(p => ({ "Nazwa projektu": p.title, "Program": p.program_name || 'Brak', "Status": getStatusConfig(p).label, "Data utworzenia": formatDate(p.created_at), "% ukończenia": `${getStatusConfig(p).progress}%`, })); const worksheet = XLSX.utils.json_to_sheet(dataToExport); const workbook = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(workbook, worksheet, "Projekty"); XLSX.writeFile(workbook, "Moje_Projekty.xlsx"); }; return (

Moje Projekty

Zarządzaj swoimi wnioskami dotacyjnymi i powracaj do starych sesji RAG.

setShowWizard(true)} style={{ padding: '1rem 2rem', fontSize: '1.05rem', background: 'linear-gradient(90deg, var(--accent-green), #3b82f6)', boxShadow: '0 0 20px rgba(16,185,129,0.3)', border: 'none', color: '#000', fontWeight: 800 }} > Nowy Projekt Dotacyjny
{/* FILTRY */}
setSearchQuery(e.target.value)} style={{ width: '100%', padding: '1rem 1rem 1rem 3rem', background: 'rgba(255,255,255,0.03)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '12px', color: '#fff', outline: 'none' }} />
{projects.length === 0 ? (
setShowWizard(true)} />
) : filteredProjects.length === 0 ? (

Brak wyników wyszukiwania

Żaden projekt nie spełnia wybranych kryteriów filtra lub wyszukiwania.

) : viewMode === 'grid' && (
{filteredProjects.slice((currentPage - 1) * itemsPerPage, currentPage * itemsPerPage).map((p, i) => { const config = getStatusConfig(p); return (
{config.icon} {config.label}
{ e.stopPropagation(); setMenuOpenId(menuOpenId === p.id ? null : p.id); }} style={{ cursor: 'pointer', padding: '0.2rem' }} >
{menuOpenId === p.id && (
)}

{p.title}

{p.program_name || "Brak wybranego programu"}
Postęp wniosku (AI) {config.progress}%
{formatDate(p.created_at)}
navigate(`/projects/${p.id}`)} style={{ display: 'flex', alignItems: 'center', gap: '0.4rem', background: 'transparent', border: 'none', color: 'var(--accent-green)', fontWeight: 800, cursor: 'pointer', fontSize: '0.95rem' }} > Otwórz Projekt
) })}
)} {filteredProjects.length > 0 && viewMode === 'list' && (
{filteredProjects.slice((currentPage - 1) * itemsPerPage, currentPage * itemsPerPage).map((p) => { const config = getStatusConfig(p); return ( ); })}
Projekt Program Status Data utworzenia Akcje
{p.title}
{p.program_name || '-'}
{config.icon} {config.label}
{formatDate(p.created_at)}
)} {filteredProjects.length > itemsPerPage && (
Strona {currentPage} z {Math.ceil(filteredProjects.length / itemsPerPage)}
)} {showWizard && setShowWizard(false)} />}
); }; export default Projects;