import React, { useState, useRef, useEffect } from 'react'; import { useAppStore, ProjectData, PostData, MediaData } from '../../store'; import { showToast } from '../Toast'; import './ProjectSelector.css'; export const ProjectSelector: React.FC = () => { const { projects, activeProject, setProjects, setActiveProject, setPosts, setMedia, setSelectedPost, setSelectedMedia, removeProject } = useAppStore(); const [isOpen, setIsOpen] = useState(false); const [showCreateModal, setShowCreateModal] = useState(false); const [showDeleteModal, setShowDeleteModal] = useState(false); const [projectToDelete, setProjectToDelete] = useState(null); const [deleteConfirmText, setDeleteConfirmText] = useState(''); const [newProjectName, setNewProjectName] = useState(''); const [newProjectDescription, setNewProjectDescription] = useState(''); const dropdownRef = useRef(null); // Load projects on mount useEffect(() => { const loadProjects = async () => { try { const allProjects = await window.electronAPI?.projects.getAll(); if (allProjects) { setProjects(allProjects as ProjectData[]); } const active = await window.electronAPI?.projects.getActive(); if (active) { setActiveProject(active as ProjectData); } } catch (error) { console.error('Failed to load projects:', error); } }; loadProjects(); }, [setProjects, setActiveProject]); // Close dropdown when clicking outside useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { setIsOpen(false); } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, []); const handleSwitchProject = async (project: ProjectData) => { if (project.id === activeProject?.id) { setIsOpen(false); return; } try { const updatedProject = await window.electronAPI?.projects.setActive(project.id); if (updatedProject) { setActiveProject(updatedProject as ProjectData); // Clear current selection and reload data setSelectedPost(null); setSelectedMedia(null); // Reload posts and media for new project const [postsResult, mediaResult] = await Promise.all([ window.electronAPI?.posts.getAll({ limit: 500, offset: 0 }), window.electronAPI?.media.getAll(), ]); // posts.getAll returns { items, hasMore, total } if (postsResult) { const { items, hasMore, total } = postsResult as { items: PostData[]; hasMore: boolean; total: number }; setPosts(items, hasMore, total); } if (mediaResult) setMedia(mediaResult as MediaData[]); showToast.success(`Switched to ${project.name}`); } } catch (error) { console.error('Failed to switch project:', error); showToast.error('Failed to switch project'); } setIsOpen(false); }; const handleCreateProject = async (e: React.FormEvent) => { e.preventDefault(); if (!newProjectName.trim()) return; try { const newProject = await window.electronAPI?.projects.create({ name: newProjectName.trim(), description: newProjectDescription.trim() || undefined, }); if (newProject) { setProjects([...projects, newProject as ProjectData]); showToast.success(`Created project "${newProjectName}"`); setNewProjectName(''); setNewProjectDescription(''); setShowCreateModal(false); // Optionally switch to the new project await handleSwitchProject(newProject as ProjectData); } } catch (error) { console.error('Failed to create project:', error); showToast.error('Failed to create project'); } }; const openDeleteModal = (e: React.MouseEvent, project: ProjectData) => { e.stopPropagation(); setProjectToDelete(project); setDeleteConfirmText(''); setShowDeleteModal(true); setIsOpen(false); }; const handleDeleteProject = async (e: React.FormEvent) => { e.preventDefault(); if (!projectToDelete || deleteConfirmText !== projectToDelete.name) return; try { const success = await window.electronAPI?.projects.deleteWithData(projectToDelete.id); if (success) { removeProject(projectToDelete.id); showToast.success(`Deleted project "${projectToDelete.name}" and all its data`); setShowDeleteModal(false); setProjectToDelete(null); setDeleteConfirmText(''); } else { showToast.error('Failed to delete project'); } } catch (error) { console.error('Failed to delete project:', error); showToast.error('Failed to delete project'); } }; const canDeleteProject = (project: ProjectData) => { // Cannot delete: default project, or the currently active project return project.id !== 'default' && project.id !== activeProject?.id; }; return (
{isOpen && (
PROJECTS
{projects.map(project => (
{canDeleteProject(project) && ( )}
))} {projects.length === 0 && (
No projects yet
)}
)} {showCreateModal && (
setShowCreateModal(false)}>
e.stopPropagation()}>

Create New Project

setNewProjectName(e.target.value)} placeholder="My Blog" autoFocus />