Add .gitignore Removed node_modules from previous commit Fix task modes Fix task modes Fix task modes Remove node_modules Update basic task modal Add notes functionality Improve UI Setup views Add scopes Fix projects layout Restructure Fix rest of the UI issues Cleanup old views Add .env to .gitignore
146 lines
4.4 KiB
TypeScript
146 lines
4.4 KiB
TypeScript
import React, { useEffect, useLayoutEffect, useState } from "react";
|
|
import { Routes, Route, useNavigate, Navigate } from "react-router-dom";
|
|
import Login from "./Login";
|
|
import Tasks from "./Tasks";
|
|
import NotFound from "./NotFound";
|
|
import ProjectDetails from "./components/Project/ProjectDetails";
|
|
import Projects from "./Projects";
|
|
import AreaDetails from "./components/Area/AreaDetails";
|
|
import Areas from "./Areas";
|
|
import TagDetails from "./components/Tag/TagDetails";
|
|
import Tags from "./Tags";
|
|
import Notes from "./Notes";
|
|
import NoteDetails from "./components/Note/NoteDetails";
|
|
import ProfileSettings from "./components/Profile/ProfileSettings";
|
|
import Layout from "./Layout";
|
|
import { DataProvider } from './contexts/DataContext'; // Import the DataProvider
|
|
import { CalendarDaysIcon } from "@heroicons/react/24/solid";
|
|
|
|
interface User {
|
|
email: string;
|
|
id: number;
|
|
}
|
|
|
|
const App: React.FC = () => {
|
|
const [currentUser, setCurrentUser] = useState<User | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
const [isDarkMode, setIsDarkMode] = useState<boolean>(() => {
|
|
const storedPreference = localStorage.getItem("isDarkMode");
|
|
if (storedPreference !== null) {
|
|
return storedPreference === "true";
|
|
} else {
|
|
return window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
}
|
|
});
|
|
|
|
const navigate = useNavigate();
|
|
|
|
useEffect(() => {
|
|
const fetchCurrentUser = async () => {
|
|
try {
|
|
const response = await fetch("/api/current_user", {
|
|
credentials: "include",
|
|
headers: {
|
|
Accept: "application/json",
|
|
},
|
|
});
|
|
const data = await response.json();
|
|
if (data.user) {
|
|
setCurrentUser(data.user);
|
|
} else {
|
|
navigate("/login");
|
|
}
|
|
} catch (err) {
|
|
console.error("Failed to fetch current user:", err);
|
|
navigate("/login");
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
fetchCurrentUser();
|
|
}, [navigate]);
|
|
|
|
useLayoutEffect(() => {
|
|
const root = document.documentElement;
|
|
if (isDarkMode) {
|
|
root.classList.add("dark");
|
|
} else {
|
|
root.classList.remove("dark");
|
|
}
|
|
}, [isDarkMode]);
|
|
|
|
useEffect(() => {
|
|
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
|
|
const handleChange = (e: MediaQueryListEvent) => {
|
|
if (localStorage.getItem("isDarkMode") === null) {
|
|
setIsDarkMode(e.matches);
|
|
}
|
|
};
|
|
|
|
mediaQuery.addEventListener("change", handleChange);
|
|
|
|
return () => {
|
|
mediaQuery.removeEventListener("change", handleChange);
|
|
};
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (currentUser && location.pathname === "/") {
|
|
const options = { path: '/tasks?type=today', title: 'Today', icon: <CalendarDaysIcon className="h-5 w-5" /> }
|
|
navigate(options.path, { state: { title: options.title, icon: options.icon }, replace: true });
|
|
}
|
|
}, [currentUser, location.pathname, navigate]);
|
|
|
|
const toggleDarkMode = () => {
|
|
const newValue = !isDarkMode;
|
|
setIsDarkMode(newValue);
|
|
localStorage.setItem("isDarkMode", JSON.stringify(newValue));
|
|
};
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="flex items-center justify-center h-screen bg-gray-100 dark:bg-gray-900">
|
|
<div className="text-xl font-semibold text-gray-700 dark:text-gray-200">
|
|
Loading...
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<DataProvider>
|
|
{currentUser ? (
|
|
<Layout
|
|
currentUser={currentUser}
|
|
isDarkMode={isDarkMode}
|
|
toggleDarkMode={toggleDarkMode}
|
|
>
|
|
<Routes>
|
|
<Route
|
|
path="/"
|
|
element={<Navigate to="/tasks" replace />}
|
|
/>
|
|
<Route path="/tasks" element={<Tasks />} />
|
|
<Route path="/projects" element={<Projects />} />
|
|
<Route path="/project/:id" element={<ProjectDetails />} />
|
|
<Route path="/areas" element={<Areas />} />
|
|
<Route path="/area/:id" element={<AreaDetails />} />
|
|
<Route path="/tags" element={<Tags />} />
|
|
<Route path="/tag/:id" element={<TagDetails />} />
|
|
<Route path="/notes" element={<Notes />} />
|
|
<Route path="/note/:id" element={<NoteDetails />} />
|
|
<Route path="/profile" element={<ProfileSettings currentUser={currentUser} />} />
|
|
<Route path="*" element={<NotFound />} />
|
|
</Routes>
|
|
</Layout>
|
|
) : (
|
|
<Login />
|
|
)}
|
|
</DataProvider>
|
|
);
|
|
};
|
|
|
|
export default App;
|