import React, { useState, useEffect } from "react"; import { useTranslation } from "react-i18next"; import { PlusIcon } from '@heroicons/react/24/outline'; import { useToast } from "./components/Shared/ToastContext"; import Navbar from "./components/Navbar"; import Sidebar from "./components/Sidebar"; import "./styles/tailwind.css"; import ProjectModal from "./components/Project/ProjectModal"; import NoteModal from "./components/Note/NoteModal"; import AreaModal from "./components/Area/AreaModal"; import TagModal from "./components/Tag/TagModal"; import SimplifiedTaskModal from "./components/Task/SimplifiedTaskModal"; import TaskModal from "./components/Task/TaskModal"; import { Note } from "./entities/Note"; import { Area } from "./entities/Area"; import { Tag } from "./entities/Tag"; import { Project } from "./entities/Project"; import { Task } from "./entities/Task"; import { User } from "./entities/User"; import { useStore } from "./store/useStore"; import { fetchNotes, createNote, updateNote } from "./utils/notesService"; import { fetchAreas, createArea, updateArea } from "./utils/areasService"; import { fetchTags, createTag, updateTag } from "./utils/tagsService"; import { fetchProjects, createProject, updateProject } from "./utils/projectsService"; import { createTask, updateTask } from "./utils/tasksService"; import { isAuthError } from "./utils/authUtils"; interface LayoutProps { currentUser: User; isDarkMode: boolean; setCurrentUser: React.Dispatch>; toggleDarkMode: () => void; children: React.ReactNode; } const Layout: React.FC = ({ currentUser, setCurrentUser, isDarkMode, toggleDarkMode, children, }) => { const { t } = useTranslation(); const { showSuccessToast } = useToast(); const [isSidebarOpen, setIsSidebarOpen] = useState(window.innerWidth >= 1024); const [isTaskModalOpen, setIsTaskModalOpen] = useState(false); const [isProjectModalOpen, setIsProjectModalOpen] = useState(false); const [isNoteModalOpen, setIsNoteModalOpen] = useState(false); const [isAreaModalOpen, setIsAreaModalOpen] = useState(false); const [isTagModalOpen, setIsTagModalOpen] = useState(false); const [taskModalType, setTaskModalType] = useState<'simplified' | 'full'>('simplified'); const [selectedNote, setSelectedNote] = useState(null); const [selectedArea, setSelectedArea] = useState(null); const [selectedTag, setSelectedTag] = useState(null); const [newTask, setNewTask] = useState(null); const { notesStore: { notes, setNotes, setLoading: setNotesLoading, setError: setNotesError, isLoading: isNotesLoading, isError: isNotesError, }, areasStore: { areas, setAreas, setLoading: setAreasLoading, setError: setAreasError, isLoading: isAreasLoading, isError: isAreasError, }, tasksStore: { setLoading: setTasksLoading, setError: setTasksError, isLoading: isTasksLoading, isError: isTasksError, }, projectsStore: { projects, setProjects, setLoading: setProjectsLoading, setError: setProjectsError, isLoading: isProjectsLoading, isError: isProjectsError, }, tagsStore: { tags, setTags, setLoading: setTagsLoading, setError: setTagsError, isLoading: isTagsLoading, isError: isTagsError, }, } = useStore(); const openTaskModal = (type: 'simplified' | 'full' = 'simplified') => { setIsTaskModalOpen(true); setTaskModalType(type); }; useEffect(() => { const handleResize = () => { setIsSidebarOpen(window.innerWidth >= 1024); }; window.addEventListener("resize", handleResize); return () => window.removeEventListener("resize", handleResize); }, []); const loadNotes = async () => { setNotesLoading(true); try { const notesData = await fetchNotes(); setNotes(notesData); } catch (error) { console.error("Error fetching notes:", error); setNotesError(true); } finally { setNotesLoading(false); } }; const loadAreas = async () => { setAreasLoading(true); try { const areasData = await fetchAreas(); setAreas(areasData); } catch (error) { console.error("Error fetching areas:", error); setAreasError(true); } finally { setAreasLoading(false); } }; useEffect(() => { loadNotes(); loadAreas(); }, []); const openNoteModal = (note: Note | null = null) => { setSelectedNote(note); setIsNoteModalOpen(true); }; const closeNoteModal = () => { setIsNoteModalOpen(false); setSelectedNote(null); }; const closeTaskModal = () => { setIsTaskModalOpen(false); setNewTask(null); }; const openProjectModal = () => { setIsProjectModalOpen(true); }; const closeProjectModal = () => { setIsProjectModalOpen(false); }; const openAreaModal = (area: Area | null = null) => { setSelectedArea(area); setIsAreaModalOpen(true); }; const closeAreaModal = () => { setIsAreaModalOpen(false); setSelectedArea(null); }; const openTagModal = (tag: Tag | null = null) => { setSelectedTag(tag); setIsTagModalOpen(true); }; const closeTagModal = () => { setIsTagModalOpen(false); setSelectedTag(null); }; const handleSaveNote = async (noteData: Note) => { try { if (noteData.id) { await updateNote(noteData.id, noteData); } else { await createNote(noteData); } loadNotes(); closeNoteModal(); } catch (error: any) { console.error("Error saving note:", error); // Don't close modal if there's an auth error (user will be redirected) if (isAuthError(error)) { return; } closeNoteModal(); } }; const handleSaveTask = async (taskData: Task) => { try { if (taskData.id) { await updateTask(taskData.id, taskData); const taskLink = ( {t('task.updated', 'Task')} {taskData.name} {t('task.updatedSuccessfully', 'updated successfully!')} ); showSuccessToast(taskLink); } else { const createdTask = await createTask(taskData); const taskLink = ( {t('task.created', 'Task')} {createdTask.name} {t('task.createdSuccessfully', 'created successfully!')} ); showSuccessToast(taskLink); } // Don't refetch all tasks here - let individual components handle their own state // This prevents unnecessary re-renders and race conditions closeTaskModal(); } catch (error: any) { console.error("Error saving task:", error); // Don't close modal if there's an auth error (user will be redirected) if (isAuthError(error)) { return; } // For other errors, still close the modal but let the error bubble up closeTaskModal(); throw error; } }; const handleCreateProject = async (name: string): Promise => { try { const newProject = await createProject({ name, active: true, }); return newProject; } catch (error) { console.error("Error creating project:", error); throw error; } }; const handleSaveProject = async (projectData: Project) => { try { if (projectData.id) { await updateProject(projectData.id, projectData); } else { await createProject(projectData); } const projectsData = await fetchProjects(); setProjects(projectsData); closeProjectModal(); } catch (error: any) { console.error("Error saving project:", error); // Don't close modal if there's an auth error (user will be redirected) if (isAuthError(error)) { return; } closeProjectModal(); } }; const handleSaveArea = async (areaData: Partial) => { try { if (areaData.id) { await updateArea(areaData.id, areaData); } else { await createArea(areaData); } loadAreas(); closeAreaModal(); } catch (error: any) { console.error("Error saving area:", error); // Don't close modal if there's an auth error (user will be redirected) if (isAuthError(error)) { return; } closeAreaModal(); } }; const handleSaveTag = async (tagData: Tag) => { try { if (tagData.id) { await updateTag(tagData.id, tagData); } else { await createTag(tagData); } const tagsData = await fetchTags(); setTags(tagsData); closeTagModal(); } catch (error: any) { console.error("Error saving tag:", error); // Don't close modal if there's an auth error (user will be redirected) if (isAuthError(error)) { return; } closeTagModal(); } }; const handleLogout = async () => { try { const response = await fetch('/api/logout', { method: 'GET', credentials: 'include', }); if (response.ok) { setCurrentUser(null); } else { console.error('Logout failed:', await response.json()); } } catch (error) { console.error('Error during logout:', error); } }; const mainContentMarginLeft = isSidebarOpen ? "ml-72" : "ml-0"; const isLoading = isNotesLoading || isAreasLoading || isTasksLoading || isProjectsLoading || isTagsLoading; const isError = isNotesError || isAreasError || isTasksError || isProjectsError || isTagsError; if (isLoading) { return (
{t('common.loading')}
); } if (isError) { return (
{t('errors.somethingWentWrong')}
); } return (
{children}
{isTaskModalOpen && ( taskModalType === 'simplified' ? ( ) : ( {}} projects={projects} onCreateProject={handleCreateProject} /> ) )} {isProjectModalOpen && ( )} {isNoteModalOpen && ( )} {isAreaModalOpen && ( )} {isTagModalOpen && ( )}
); }; export default Layout;