diff --git a/src/electron/ipc/ipcHandler.ts b/src/electron/ipc/ipcHandler.ts index 221fc58..fab46c6 100644 --- a/src/electron/ipc/ipcHandler.ts +++ b/src/electron/ipc/ipcHandler.ts @@ -82,7 +82,7 @@ ipcMain.handle("get-project-list", async () => { // console.log(" API /list/project response:", res); - // ✅ Return only serializable data + // Return only serializable data return JSON.parse(JSON.stringify(res)); } catch (error: any) { console.error(" get-project-list error:", error); @@ -180,7 +180,7 @@ ipcMain.handle("get-project-logs", async (event, { projectID, fromDate, toDate } let url = `/report/project/${projectID}`; - console.log("url" , url) + // 🧠 Append query params only if dates are provided if (fromDate && toDate) { @@ -191,6 +191,8 @@ ipcMain.handle("get-project-logs", async (event, { projectID, fromDate, toDate } url += `?${params.toString()}`; } + console.log("url" , url) + const response = await api.get(url); console.log("✅ response-logs:", response); @@ -201,3 +203,18 @@ ipcMain.handle("get-project-logs", async (event, { projectID, fromDate, toDate } } }); +ipcMain.handle("forgot-password", async (event, email: string) => { + try { + const response = await api.post("/forgot-password", { email }); + console.log(response , "Line response") + if (response) { + return { success: true, message: response.data?.message }; + } + } catch (error: any) { + console.error("Forgot password error:", error.response?.data || error.message); + return { + success: false, + message: error.response?.data?.message || "Something went wrong", + }; + } +}); diff --git a/src/electron/main.ts b/src/electron/main.ts index c0826e0..4083518 100644 --- a/src/electron/main.ts +++ b/src/electron/main.ts @@ -90,6 +90,8 @@ function handleCloseEvents(mainWindow: BrowserWindow) { }); } + + export function getMainWindow() { return mainWindow; } \ No newline at end of file diff --git a/src/electron/preload.cts b/src/electron/preload.cts index 2072609..3c4fac2 100644 --- a/src/electron/preload.cts +++ b/src/electron/preload.cts @@ -5,6 +5,7 @@ console.log("setting PreLoad") contextBridge.exposeInMainWorld('electron', { login: (credentials: { email: string, password: string }) => ipcRenderer.invoke('login-request', credentials), logout: () => ipcRenderer.invoke('logout-request'), + forgotPassword: (email: string) => ipcRenderer.invoke("forgot-password", email), getToken: (TOKEN_KEY: "ACCESS_TOKEN" | "REFRESH_TOKEN") => ipcRenderer.invoke('get-token', TOKEN_KEY), uploadScreenshot: () => ipcRenderer.invoke('upload-screenshot'), getActiveAppDetails: () => ipcRenderer.invoke('get-active-app'), diff --git a/src/ui/App.tsx b/src/ui/App.tsx index c5f0a6e..7a66012 100644 --- a/src/ui/App.tsx +++ b/src/ui/App.tsx @@ -1,46 +1,38 @@ -import React, { useEffect, useRef, useState } from "react"; +import React, { useEffect } from "react"; import { BrowserRouter, Route, Routes, Navigate } from "react-router-dom"; +import { useDispatch, useSelector } from "react-redux"; +import { AppDispatch, RootState } from "./store"; +import { loadUserFromStorage } from "./store/userSlice"; + import Login from "./pages/Login"; import Dashboard from "./pages/Dashboard"; import Projects from "./pages/Projects/Projects"; import DashboardLayout from "./layout/DashboardLayout"; -import ProtectedRoute from "./routes/ProtectedRoute" +import ProtectedRoute from "./routes/ProtectedRoute"; import PublicRoute from "./routes/PublicRoute"; -import { ThemeToggle } from "./helper/ThemeToggle" +import { ThemeToggle } from "./helper/ThemeToggle"; import LeaveAndAdjustments from "./pages/LeaveAndAdjustments/LeaveAndAdjustments"; import Timesheets from "./pages/Timesheets/Timesheets"; import Settings from "./pages/Settings/Settings"; import HelpSupport from "./pages/HelpSupport/HelpSupport"; -interface User { - email: string; - password: string; -} - - const App: React.FC = () => { const theme = ThemeToggle(); - const [user, setUser] = useState(null); + const dispatch = useDispatch(); + // ✅ Get user directly from Redux + const user = useSelector((state: RootState) => state.user.user); - // Apply theme useEffect(() => { document.documentElement.setAttribute("data-theme", theme); }, [theme]); - // Load user from localStorage + // ✅ Load user from localStorage into Redux when app starts useEffect(() => { - const storedUser = localStorage.getItem("user"); - if (storedUser) { - try { - setUser(JSON.parse(storedUser)); - } catch (err) { - console.error("Invalid user in localStorage", err); - } - } - }, []); + dispatch(loadUserFromStorage()); + }, [dispatch]); - + console.log("App.tsx → user:", user); return ( @@ -50,7 +42,7 @@ const App: React.FC = () => { path="/login" element={ - + } /> @@ -60,7 +52,7 @@ const App: React.FC = () => { path="/dashboard" element={ - + } > @@ -82,4 +74,4 @@ const App: React.FC = () => { ); }; -export default App; \ No newline at end of file +export default App; diff --git a/src/ui/layout/DashboardLayout.tsx b/src/ui/layout/DashboardLayout.tsx index 445d610..a55c857 100644 --- a/src/ui/layout/DashboardLayout.tsx +++ b/src/ui/layout/DashboardLayout.tsx @@ -28,10 +28,17 @@ import { useActiveAppTracker } from "../hooks/useActiveAppTracker"; import { useDispatch, useSelector } from "react-redux"; import { RootState } from "../store"; import { setProjectsDataSlice, setProjectSummarySlice, setSelectedProjectSlice } from "../store/projectSlice"; +import { clearUser } from "../store/userSlice"; + +// interface User { +// email: string; +// password: string; +// } interface User { + id: string; + name: string; email: string; - password: string; } // Updated Project interface to match the JSON structure @@ -70,12 +77,7 @@ export interface TrackingState { projectsData: Project[]; } -interface DashboardLayoutProps { - user: User | null; - setUser: React.Dispatch>; -} - -const DashboardLayout: React.FC = ({ setUser }) => { +const DashboardLayout = () => { const [isSidebarOpen, setSidebarOpen] = useState(true); const [isMobile, setIsMobile] = useState(window.innerWidth < 768); const navigate = useNavigate(); @@ -167,23 +169,28 @@ const DashboardLayout: React.FC = ({ setUser }) => { setSelectedProject(firstProject); dispatch(setSelectedProjectSlice(firstProject)); + // Convert totalWorkMs to seconds for elapsedTime + const initialElapsedTime = Math.floor(firstProject.totalWorkMs / 1000); + // Initialize tracking map for that project setProjectTrackingMap((prev) => { const updated = new Map(prev); if (!updated.has(firstProject.projectId)) { updated.set(firstProject.projectId, { - elapsedTime: 0, + elapsedTime:initialElapsedTime, startTime: null, endTime: null, isTracking: false, isPaused: false, pauseStartTime: null, totalPausedTime: 0, - accumulatedTime: 0, + accumulatedTime: initialElapsedTime, }); } return updated; }); + // Set the initial elapsed time in the component state + setElapsedTime(initialElapsedTime); } } else { console.warn("⚠️ Unexpected response:", res); @@ -202,13 +209,15 @@ const DashboardLayout: React.FC = ({ setUser }) => { const handleLogout = async () => { try { - const res = await window.electron.logout(); - localStorage.removeItem("user"); - setUser(null); + const res = await window.electron.logout(); + if (res) { + dispatch(clearUser()); // ✅ Removes from Redux + localStorage navigate("/login"); - } catch (error) { - console.error("Logout error:", error); } + } catch (error) { + console.error("Logout error:", error); + } + }; // Sync global states with selected project tracking data @@ -432,28 +441,52 @@ const DashboardLayout: React.FC = ({ setUser }) => { // Handle project selection const handleProjectSelect = (projectId: string) => { - if (isTracking) return; + if (isTracking) return; // prevent switching while tracking - const project = projectsData.find((p) => p.projectId === projectId); - if (project) { - setSelectedProject(project); + const project = projectsData.find((p) => p.projectId === projectId); + if (!project) return; - const projectData = projectTrackingMap.get(projectId); - if (projectData) { - setElapsedTime(projectData.elapsedTime); - setStartTime(projectData.startTime); - setEndTime(projectData.endTime); - setIsTracking(projectData.isTracking); - setIsPaused(projectData.isPaused); - } else { - setElapsedTime(0); - setStartTime(null); - setEndTime(null); - setIsTracking(false); - setIsPaused(false); - } + setSelectedProject(project); + dispatch(setSelectedProjectSlice(project)); + + const existingData = projectTrackingMap.get(projectId); + + setProjectTrackingMap((prev) => { + const updated = new Map(prev); + + // If project is not yet tracked, initialize it with totalWorkMs + if (!existingData) { + const initialElapsedTime = Math.floor((project.totalWorkMs || 0) / 1000); + updated.set(projectId, { + elapsedTime: initialElapsedTime, + startTime: null, + endTime: null, + isTracking: false, + isPaused: false, + pauseStartTime: null, + totalPausedTime: 0, + accumulatedTime: initialElapsedTime, + }); + + // Update component-level state + setElapsedTime(initialElapsedTime); + setStartTime(null); + setEndTime(null); + setIsTracking(false); + setIsPaused(false); + } else { + // If already tracked, restore its state + setElapsedTime(existingData.elapsedTime); + setStartTime(existingData.startTime); + setEndTime(existingData.endTime); + setIsTracking(existingData.isTracking); + setIsPaused(existingData.isPaused); } - }; + + return updated; + }); +}; + const trackingState: TrackingState = { selectedProject, @@ -557,11 +590,11 @@ const DashboardLayout: React.FC = ({ setUser }) => { } {/* Layout Body */} -
+
{/* Sidebar */}