import { useState, useEffect, useContext } from 'react' import { useNavigate } from 'react-router-dom' import { Plus, Search, FolderKanban, LayoutGrid, GanttChart } from 'lucide-react' import { AppContext } from '../App' import { api } from '../utils/api' import { useAuth } from '../contexts/AuthContext' import ProjectCard from '../components/ProjectCard' import Modal from '../components/Modal' import InteractiveTimeline from '../components/InteractiveTimeline' import { SkeletonCard } from '../components/SkeletonLoader' const EMPTY_PROJECT = { name: '', description: '', brand_id: '', status: 'active', owner_id: '', start_date: '', due_date: '', } export default function Projects() { const navigate = useNavigate() const { teamMembers, brands } = useContext(AppContext) const { permissions } = useAuth() const [projects, setProjects] = useState([]) const [loading, setLoading] = useState(true) const [showModal, setShowModal] = useState(false) const [formData, setFormData] = useState(EMPTY_PROJECT) const [searchTerm, setSearchTerm] = useState('') const [view, setView] = useState('timeline') // 'grid' | 'timeline' useEffect(() => { loadProjects() }, []) const loadProjects = async () => { try { const res = await api.get('/projects') setProjects(res.data || res || []) } catch (err) { console.error('Failed to load projects:', err) } finally { setLoading(false) } } const handleCreate = async () => { try { const data = { name: formData.name, description: formData.description, brand_id: formData.brand_id ? Number(formData.brand_id) : null, owner_id: formData.owner_id ? Number(formData.owner_id) : null, status: formData.status, start_date: formData.start_date || null, due_date: formData.due_date || null, } await api.post('/projects', data) setShowModal(false) setFormData(EMPTY_PROJECT) loadProjects() } catch (err) { console.error('Create failed:', err) } } const filtered = projects.filter(p => { if (searchTerm && !p.name?.toLowerCase().includes(searchTerm.toLowerCase())) return false return true }) if (loading) { return (
{[...Array(6)].map((_, i) => )}
) } return (
{/* Toolbar */}
setSearchTerm(e.target.value)} className="w-full pl-10 pr-4 py-2 text-sm border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-primary/20 focus:border-brand-primary bg-white" />
{/* View switcher */}
{[ { id: 'grid', icon: LayoutGrid, label: 'Grid' }, { id: 'timeline', icon: GanttChart, label: 'Timeline' }, ].map(v => ( ))}
{permissions?.canCreateProjects && ( )}
{/* Content */} {filtered.length === 0 ? (

No projects yet

Create your first project to start organizing work

) : view === 'grid' ? (
{filtered.map(project => ( ))}
) : ( ({ id: project._id || project.id, label: project.name, description: project.description, startDate: project.startDate || project.start_date || project.createdAt, endDate: project.dueDate || project.due_date, status: project.status, priority: project.priority, assigneeName: project.ownerName || project.owner_name, thumbnailUrl: project.thumbnail_url || project.thumbnailUrl, tags: [project.status, project.priority].filter(Boolean), })} onDateChange={async (projectId, { startDate, endDate }) => { try { await api.patch(`/projects/${projectId}`, { start_date: startDate, due_date: endDate }) } catch (err) { console.error('Timeline date update failed:', err) } finally { loadProjects() } }} onItemClick={(project) => { navigate(`/projects/${project._id || project.id}`) }} /> )} {/* Create Modal */} setShowModal(false)} title="Create New Project" size="md">
setFormData(f => ({ ...f, name: e.target.value }))} className="w-full px-3 py-2 text-sm border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-primary/20 focus:border-brand-primary" placeholder="Project name" />