From 17cba48ef875d37490b6f4e4d8563caa8aa12a43 Mon Sep 17 00:00:00 2001 From: Chris Date: Tue, 16 Sep 2025 12:07:57 +0300 Subject: [PATCH] Fix project list empty (#335) * Move all fetch project actions to layout * fixup! Move all fetch project actions to layout --- frontend/Layout.tsx | 16 +++++++-- frontend/components/Note/NoteDetails.tsx | 22 ++++--------- frontend/components/Notes.tsx | 18 ++--------- frontend/components/Projects.tsx | 14 +------- frontend/components/Tag/TagDetails.tsx | 41 +++++++++--------------- frontend/components/Tasks.tsx | 20 ++++-------- 6 files changed, 45 insertions(+), 86 deletions(-) diff --git a/frontend/Layout.tsx b/frontend/Layout.tsx index 5ca6e24..05a83d5 100644 --- a/frontend/Layout.tsx +++ b/frontend/Layout.tsx @@ -89,8 +89,20 @@ const Layout: React.FC = ({ }, []); useEffect(() => { - // Layout no longer loads global data - }, []); + // Load projects into global store if not already loaded + const loadProjects = async () => { + if (projects.length === 0 && !isProjectsLoading) { + try { + const projectsData = await fetchProjects(); + setProjects(projectsData); + } catch (error) { + console.error('Failed to load projects in Layout:', error); + } + } + }; + + loadProjects(); + }, [projects.length, isProjectsLoading, setProjects]); const openNoteModal = (note: Note | null = null) => { setSelectedNote(note); diff --git a/frontend/components/Note/NoteDetails.tsx b/frontend/components/Note/NoteDetails.tsx index 2a80698..a7e6319 100644 --- a/frontend/components/Note/NoteDetails.tsx +++ b/frontend/components/Note/NoteDetails.tsx @@ -15,8 +15,8 @@ import { deleteNote as apiDeleteNote, updateNote as apiUpdateNote, } from '../../utils/notesService'; -import { createProject, fetchProjects } from '../../utils/projectsService'; -import { Project } from '../../entities/Project'; +import { createProject } from '../../utils/projectsService'; +import { useStore } from '../../store/useStore'; const NoteDetails: React.FC = () => { const { uidSlug } = useParams<{ uidSlug: string }>(); @@ -27,7 +27,8 @@ const NoteDetails: React.FC = () => { const [noteToDelete, setNoteToDelete] = useState(null); const [isLoading, setIsLoading] = useState(true); const [isError, setIsError] = useState(false); - const [projects, setProjects] = useState([]); + const projects = useStore((state: any) => state.projectsStore.projects); + const { setProjects } = useStore((state: any) => state.projectsStore); const navigate = useNavigate(); // Dispatch global modal events @@ -51,18 +52,7 @@ const NoteDetails: React.FC = () => { fetchNote(); }, [uidSlug]); - // Load projects for the modal - useEffect(() => { - const loadProjects = async () => { - try { - const fetchedProjects = await fetchProjects('all', ''); - setProjects(fetchedProjects); - } catch (error) { - console.error('Error loading projects:', error); - } - }; - loadProjects(); - }, []); + // Projects are now loaded by Layout component into global store const handleDeleteNote = async () => { if (!noteToDelete) return; @@ -101,7 +91,7 @@ const NoteDetails: React.FC = () => { name, priority: 'low', }); - setProjects((prev) => [...prev, newProject]); + setProjects([...projects, newProject]); return newProject; } catch (error) { console.error('Error creating project:', error); diff --git a/frontend/components/Notes.tsx b/frontend/components/Notes.tsx index af56b1b..e568715 100644 --- a/frontend/components/Notes.tsx +++ b/frontend/components/Notes.tsx @@ -12,7 +12,7 @@ import { deleteNote as apiDeleteNote, } from '../utils/notesService'; import { useStore } from '../store/useStore'; -import { createProject, fetchProjects } from '../utils/projectsService'; +import { createProject } from '../utils/projectsService'; const Notes: React.FC = () => { const { t } = useTranslation(); @@ -28,7 +28,6 @@ const Notes: React.FC = () => { const { notes, isLoading, isError, hasLoaded, loadNotes, setNotes } = useStore((state) => state.notesStore); const projects = useStore((state) => state.projectsStore.projects); - const { setProjects } = useStore((state) => state.projectsStore); useEffect(() => { if (!hasLoaded && !isLoading && !isError) { @@ -36,20 +35,7 @@ const Notes: React.FC = () => { } }, [hasLoaded, isLoading, isError, loadNotes]); - // Load projects if not available - force load every time for debugging - useEffect(() => { - const loadProjectsIfNeeded = async () => { - try { - // Fetch all projects (active and inactive) - const fetchedProjects = await fetchProjects('all', ''); - setProjects(fetchedProjects); - } catch (error) { - console.error('Error loading projects:', error); - } - }; - - loadProjectsIfNeeded(); - }, []); // Remove dependencies to force it to run once + // Projects are now loaded by Layout component into global store // Sort options for notes const sortOptions: SortOption[] = [ diff --git a/frontend/components/Projects.tsx b/frontend/components/Projects.tsx index 9349d44..0f52295 100644 --- a/frontend/components/Projects.tsx +++ b/frontend/components/Projects.tsx @@ -118,19 +118,7 @@ const Projects: React.FC = () => { loadAreas(); }, []); - useEffect(() => { - const loadProjects = async () => { - try { - const projectsData = await fetchProjects(); - setProjects(projectsData); - } catch (error) { - console.error('Failed to fetch projects:', error); - setProjectsError(true); - } - }; - - loadProjects(); - }, []); + // Projects are now loaded by Layout component into global store // Modal state tracking removed after fixing the issue diff --git a/frontend/components/Tag/TagDetails.tsx b/frontend/components/Tag/TagDetails.tsx index fc142ef..243db84 100644 --- a/frontend/components/Tag/TagDetails.tsx +++ b/frontend/components/Tag/TagDetails.tsx @@ -16,6 +16,7 @@ import TaskList from '../Task/TaskList'; import ProjectItem from '../Project/ProjectItem'; import { Tag } from '../../entities/Tag'; +import { useStore } from '../../store/useStore'; const TagDetails: React.FC = () => { const { t } = useTranslation(); @@ -23,10 +24,19 @@ const TagDetails: React.FC = () => { const [tag, setTag] = useState(null); const [tasks, setTasks] = useState([]); const [notes, setNotes] = useState([]); - const [projects, setProjects] = useState([]); + const allProjects = useStore((state: any) => state.projectsStore.projects); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + // Filter projects by current tag + const projects = allProjects.filter( + (project: any) => + project.tags && + project.tags.some( + (projectTag: any) => projectTag.name === tag?.name + ) + ); + // State for ProjectItem components const [activeDropdown, setActiveDropdown] = useState(null); const [hoveredNoteId, setHoveredNoteId] = useState(null); @@ -45,16 +55,10 @@ const TagDetails: React.FC = () => { setTag(tagData); // Now fetch entities that have this tag using the tag name - const [tasksResponse, notesResponse, projectsResponse] = - await Promise.all([ - fetch( - `/api/tasks?tag=${encodeURIComponent(tagData.name)}` - ), - fetch( - `/api/notes?tag=${encodeURIComponent(tagData.name)}` - ), - fetch(`/api/projects`), // Projects API doesn't support tag filtering yet - ]); + const [tasksResponse, notesResponse] = await Promise.all([ + fetch(`/api/tasks?tag=${encodeURIComponent(tagData.name)}`), + fetch(`/api/notes?tag=${encodeURIComponent(tagData.name)}`), + ]); if (tasksResponse.ok) { const tasksData = await tasksResponse.json(); @@ -66,20 +70,7 @@ const TagDetails: React.FC = () => { setNotes(notesData || []); } - if (projectsResponse.ok) { - const projectsData = await projectsResponse.json(); - // Filter projects client-side since API doesn't support tag filtering - const allProjects = - projectsData.projects || projectsData || []; - const filteredProjects = allProjects.filter( - (project: any) => - project.tags && - project.tags.some( - (tag: any) => tag.name === tagData.name - ) - ); - setProjects(filteredProjects); - } + // Projects are now filtered from global store } catch { setError(t('tags.error')); } finally { diff --git a/frontend/components/Tasks.tsx b/frontend/components/Tasks.tsx index 9ff388c..697b14e 100644 --- a/frontend/components/Tasks.tsx +++ b/frontend/components/Tasks.tsx @@ -7,7 +7,6 @@ import GroupedTaskList from './Task/GroupedTaskList'; import NewTask from './Task/NewTask'; import SortFilter from './Shared/SortFilter'; import { Task } from '../entities/Task'; -import { Project } from '../entities/Project'; import { getTitleAndIcon } from './Task/getTitleAndIcon'; import { getDescription } from './Task/getDescription'; import { @@ -15,6 +14,7 @@ import { toggleTaskToday, GroupedTasks, } from '../utils/tasksService'; +import { useStore } from '../store/useStore'; import { useToast } from './Shared/ToastContext'; import { SortOption } from './Shared/SortFilterButton'; import { @@ -44,7 +44,7 @@ const Tasks: React.FC = () => { const { showSuccessToast } = useToast(); const { isSidebarOpen } = useSidebar(); const [tasks, setTasks] = useState([]); - const [projects, setProjects] = useState([]); + const projects = useStore((state: any) => state.projectsStore.projects); const [groupedTasks, setGroupedTasks] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); @@ -184,12 +184,9 @@ const Tasks: React.FC = () => { const searchParams = allTasksUrl.toString(); - const [tasksResponse, projectsResponse] = await Promise.all([ - fetch( - `/api/tasks?${searchParams}${tagId ? `&tag=${tagId}` : ''}` - ), - fetch('/api/projects'), - ]); + const tasksResponse = await fetch( + `/api/tasks?${searchParams}${tagId ? `&tag=${tagId}` : ''}` + ); if (tasksResponse.ok) { const tasksData = await tasksResponse.json(); @@ -199,12 +196,7 @@ const Tasks: React.FC = () => { throw new Error('Failed to fetch tasks.'); } - if (projectsResponse.ok) { - const projectsData = await projectsResponse.json(); - setProjects(projectsData?.projects || []); - } else { - throw new Error('Failed to fetch projects.'); - } + // Projects are now loaded by Layout component into global store } catch (error) { setError((error as Error).message); } finally {