Merge branch 'new-window-v2' of http://git.hicglobalsolutions.in/BhawanSingh/va-tracker - 3:22 - 17Dec
This commit is contained in:
commit
05e706e150
|
|
@ -16,16 +16,16 @@ export const registerIPCHandlers = (mainWindow: BrowserWindow) => {
|
||||||
|
|
||||||
ipcMain.handle("GET_BASE_URL", () => {
|
ipcMain.handle("GET_BASE_URL", () => {
|
||||||
return getBaseURL();
|
return getBaseURL();
|
||||||
});
|
});
|
||||||
|
|
||||||
// UPDATE Base URL
|
// UPDATE Base URL
|
||||||
ipcMain.handle("UPDATE_BASE_URL", (event, url: string) => {
|
ipcMain.handle("UPDATE_BASE_URL", (event, url: string) => {
|
||||||
setBaseURL(url);
|
setBaseURL(url);
|
||||||
return {
|
return {
|
||||||
updated: true,
|
updated: true,
|
||||||
baseURL: url
|
baseURL: url
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle("get-token", async (_, key: "ACCESS_TOKEN" | "REFRESH_TOKEN") => {
|
ipcMain.handle("get-token", async (_, key: "ACCESS_TOKEN" | "REFRESH_TOKEN") => {
|
||||||
return await getToken(key);
|
return await getToken(key);
|
||||||
|
|
@ -168,39 +168,30 @@ ipcMain.handle("stop-project-tracking", async (event, payload) => {
|
||||||
// tabs opening and closing when and tracking start
|
// tabs opening and closing when and tracking start
|
||||||
ipcMain.handle("tracking-tabs-opening-and-closing", async (event, payload) => {
|
ipcMain.handle("tracking-tabs-opening-and-closing", async (event, payload) => {
|
||||||
try {
|
try {
|
||||||
console.log("🟦 IPC tracking-tabs-opening-and-closing:", payload);
|
console.log("🟦 IPC received payload:", payload);
|
||||||
|
|
||||||
let response;
|
const response = await api.post("/log/project/track", payload);
|
||||||
if (!payload.id) {
|
|
||||||
// POST new log (new tab opened)
|
|
||||||
response = await api.post("/log/project/track", {
|
|
||||||
logId: payload.logId,
|
|
||||||
projectId: payload.projectId,
|
|
||||||
title: payload.title,
|
|
||||||
appName: payload.appName,
|
|
||||||
});
|
|
||||||
console.log(" New log created:", response);
|
|
||||||
} else {
|
|
||||||
// PATCH existing log (same tab, update)
|
|
||||||
response = await api.patch("/log/project/track", {
|
|
||||||
id: payload.id,
|
|
||||||
logId: payload.logId,
|
|
||||||
projectId: payload.projectId,
|
|
||||||
title: payload.title,
|
|
||||||
appName: payload.appName,
|
|
||||||
});
|
|
||||||
console.log("Log updated:", response);
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
console.log("🟩 Log track API success:", response.data);
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: response.data,
|
||||||
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error syncing project tab tracking:", error);
|
console.error("❌ Log track API error:", error);
|
||||||
throw error;
|
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: "Something went wrong",
|
||||||
|
error,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ipcMain.handle("get-project-logs", async (event, { projectID, fromDate, toDate }) => {
|
ipcMain.handle("get-project-logs", async (event, { projectID, fromDate, toDate }) => {
|
||||||
try {
|
try {
|
||||||
console.log("📥 get-project-logs called with:", { projectID, fromDate, toDate });
|
console.log("📥 get-project-logs called with:", { projectID, fromDate, toDate });
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,14 @@
|
||||||
|
// hooks/useActiveAppTracker.tsx
|
||||||
import { useEffect, useRef, useCallback } from "react";
|
import { useEffect, useRef, useCallback } from "react";
|
||||||
import { useDispatch } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { setActiveWindow, WindowInfo } from "../store/windowSlice";
|
import { RootState } from "../store";
|
||||||
|
import { WindowInfo } from "../store/windowSlice";
|
||||||
|
import {
|
||||||
|
initializeProjectLogs,
|
||||||
|
updateWindowLog,
|
||||||
|
clearProjectLogs,
|
||||||
|
debugLogsState,
|
||||||
|
} from "../store/logsSlice";
|
||||||
|
|
||||||
interface DailyHours {
|
interface DailyHours {
|
||||||
todayTotal: string;
|
todayTotal: string;
|
||||||
|
|
@ -16,273 +24,253 @@ export const useActiveAppTracker = (
|
||||||
onLimitExceeded?: () => void
|
onLimitExceeded?: () => void
|
||||||
) => {
|
) => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
const { logsByProject } = useSelector((state: RootState) => state.logs);
|
||||||
const lastActiveRef = useRef<WindowInfo | null>(null);
|
const lastActiveRef = useRef<WindowInfo | null>(null);
|
||||||
const lastLogIdRef = useRef<string | null>(null);
|
|
||||||
const lastTitleRef = useRef<string | null>(null);
|
const lastTitleRef = useRef<string | null>(null);
|
||||||
const intervalIdRef = useRef<number | null>(null);
|
const lastAppNameRef = useRef<string | null>(null);
|
||||||
const finalizeTrackingRef = useRef<(() => Promise<void>) | null>(null);
|
const intervalIdRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||||
const limitCheckedRef = useRef<boolean>(false);
|
const limitCheckedRef = useRef(false);
|
||||||
const isStoppingRef = useRef<boolean>(false); // ✅ Flag to prevent new requests
|
const isStoppingRef = useRef(false);
|
||||||
|
const checkCountRef = useRef(0);
|
||||||
|
|
||||||
const canSend = !!(activeProjectLogId && projectId && !isStoppingRef.current);
|
// Initialize logs when tracking starts
|
||||||
|
useEffect(() => {
|
||||||
// ✅ Parse time string to minutes
|
if (isTracking && activeProjectLogId && projectId) {
|
||||||
const parseTimeToMinutes = (timeStr: string): number => {
|
// Initialize fresh logs for this tracking session
|
||||||
const hourMatch = timeStr.match(/(\d+)h/);
|
dispatch(
|
||||||
const minMatch = timeStr.match(/(\d+)m/);
|
initializeProjectLogs({
|
||||||
|
|
||||||
const hours = hourMatch ? parseInt(hourMatch[1]) : 0;
|
|
||||||
const minutes = minMatch ? parseInt(minMatch[1]) : 0;
|
|
||||||
|
|
||||||
return hours * 60 + minutes;
|
|
||||||
};
|
|
||||||
|
|
||||||
// ✅ Check if daily limit is exceeded
|
|
||||||
const checkDailyLimit = (dailyHours?: DailyHours): boolean => {
|
|
||||||
if (!dailyHours) return false;
|
|
||||||
|
|
||||||
const todayTotalMins = parseTimeToMinutes(dailyHours.todayTotal);
|
|
||||||
const dailyLimitMins = parseTimeToMinutes(dailyHours.dailyLimit);
|
|
||||||
const remainingMins = parseTimeToMinutes(dailyHours.remaining);
|
|
||||||
|
|
||||||
console.log("⏰ Daily Limit Check:", {
|
|
||||||
todayTotal: `${todayTotalMins} mins`,
|
|
||||||
dailyLimit: `${dailyLimitMins} mins`,
|
|
||||||
remaining: `${remainingMins} mins`,
|
|
||||||
exceeded: remainingMins <= 0
|
|
||||||
});
|
|
||||||
|
|
||||||
if (remainingMins <= 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
// ✅ Send activity log (POST for new, PATCH for update)
|
|
||||||
const sendActivityLog = async (
|
|
||||||
projectLogId: string,
|
|
||||||
projectId: string,
|
|
||||||
title: string,
|
|
||||||
appName: string,
|
|
||||||
isNew: boolean
|
|
||||||
) => {
|
|
||||||
// ✅ CRITICAL: Block all requests when stopping
|
|
||||||
if (isStoppingRef.current) {
|
|
||||||
console.log("🚫 Stopping in progress — blocking activity log");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!canSend) {
|
|
||||||
console.log("🚫 Skipping API — missing required IDs");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const payload: {
|
|
||||||
id?: string;
|
|
||||||
logId: string;
|
|
||||||
projectId: string;
|
|
||||||
title: string;
|
|
||||||
appName: string;
|
|
||||||
} = {
|
|
||||||
logId: projectLogId,
|
|
||||||
projectId,
|
projectId,
|
||||||
title,
|
logId: activeProjectLogId,
|
||||||
appName,
|
})
|
||||||
};
|
);
|
||||||
|
console.log(` Logs initialized for project: ${projectName} (${projectId})`);
|
||||||
|
|
||||||
if (!isNew && lastLogIdRef.current) {
|
// Reset refs
|
||||||
payload.id = lastLogIdRef.current;
|
lastActiveRef.current = null;
|
||||||
|
lastTitleRef.current = null;
|
||||||
|
lastAppNameRef.current = null;
|
||||||
|
checkCountRef.current = 0;
|
||||||
|
isStoppingRef.current = false;
|
||||||
}
|
}
|
||||||
|
}, [isTracking, activeProjectLogId, projectId, projectName, dispatch]);
|
||||||
|
|
||||||
console.log(isNew ? "➡️ POST new log" : "✏️ PATCH update log", payload);
|
// Stop tracking intervals
|
||||||
|
const stopIntervals = useCallback(() => {
|
||||||
const res = await window.electron.trackingTabsOpeningClosing(payload);
|
|
||||||
|
|
||||||
console.log("📊 Activity log response:", res);
|
|
||||||
|
|
||||||
// Store the log ID for future updates
|
|
||||||
if (isNew && res?.data?._id) {
|
|
||||||
lastLogIdRef.current = res.data._id;
|
|
||||||
console.log("💾 Stored log ID:", res.data._id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ✅ Check daily limit after every API call
|
|
||||||
if (res?.dailyHours) {
|
|
||||||
const limitExceeded = checkDailyLimit(res.dailyHours);
|
|
||||||
|
|
||||||
if (limitExceeded && !limitCheckedRef.current) {
|
|
||||||
limitCheckedRef.current = true;
|
|
||||||
console.warn("🚨 Daily limit exceeded!");
|
|
||||||
|
|
||||||
if (onLimitExceeded) {
|
|
||||||
setTimeout(() => {
|
|
||||||
onLimitExceeded();
|
|
||||||
}, 500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
} catch (error) {
|
|
||||||
console.error("❌ Activity log error:", error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// ✅ Stop the interval immediately
|
|
||||||
const stopInterval = useCallback(() => {
|
|
||||||
console.log("🛑 Stopping activity tracking interval...");
|
|
||||||
if (intervalIdRef.current) {
|
if (intervalIdRef.current) {
|
||||||
clearInterval(intervalIdRef.current);
|
clearInterval(intervalIdRef.current);
|
||||||
intervalIdRef.current = null;
|
intervalIdRef.current = null;
|
||||||
console.log("✅ Interval cleared");
|
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// ✅ Send final PATCH before stopping (called by handleStopTracking)
|
// Debug function to check logs state
|
||||||
const sendFinalPatch = useCallback(async () => {
|
const debugCurrentLogs = useCallback(() => {
|
||||||
console.log("=".repeat(60));
|
if (projectId) {
|
||||||
console.log("🛑 FINAL PATCH INITIATED");
|
dispatch(debugLogsState({ projectId }));
|
||||||
console.log("=".repeat(60));
|
}
|
||||||
|
}, [projectId, dispatch]);
|
||||||
|
|
||||||
// ✅ STEP 1: Set stopping flag IMMEDIATELY
|
// Finalize and send all logs when stopping tracking
|
||||||
|
const finalizeAndSendLogs = useCallback(
|
||||||
|
async (vaId: string): Promise<boolean> => {
|
||||||
|
// console.log("=".repeat(60));
|
||||||
|
console.log(" FINALIZING AND SENDING ALL LOGS");
|
||||||
|
// console.log("=".repeat(60));
|
||||||
|
|
||||||
|
// Set stopping flag
|
||||||
isStoppingRef.current = true;
|
isStoppingRef.current = true;
|
||||||
console.log("🔒 Stopping flag set — no new requests allowed");
|
|
||||||
|
|
||||||
// ✅ STEP 2: Stop the interval IMMEDIATELY
|
// Stop intervals immediately
|
||||||
stopInterval();
|
stopIntervals();
|
||||||
|
|
||||||
if (!activeProjectLogId || !projectId) {
|
if (!activeProjectLogId || !projectId) {
|
||||||
console.log("⚠️ Skipping final PATCH — no active project log ID");
|
console.log("⚠️ Skipping — no active project");
|
||||||
return;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const projectLogs = logsByProject[projectId];
|
||||||
|
if (!projectLogs || projectLogs.logs.length === 0) {
|
||||||
|
console.log("ℹ️ No logs found for project", projectId);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ STEP 3: Send ONE final PATCH for the last active window
|
|
||||||
if (lastLogIdRef.current && lastTitleRef.current && lastActiveRef.current) {
|
|
||||||
try {
|
try {
|
||||||
console.log("📊 Final Activity Details:");
|
const nowUTC = new Date().toISOString();
|
||||||
console.log(` • Window: ${lastTitleRef.current}`);
|
|
||||||
console.log(` • App: ${lastActiveRef.current.owner.name}`);
|
|
||||||
console.log(` • Log ID: ${lastLogIdRef.current}`);
|
|
||||||
console.log(` • Project: ${projectId}`);
|
|
||||||
|
|
||||||
const result = await sendActivityLog(
|
// Finalize the last log entry
|
||||||
activeProjectLogId,
|
const allLogs = [...projectLogs.logs];
|
||||||
|
// if (allLogs.length > 0) {
|
||||||
|
// const lastLog = allLogs[allLogs.length - 1];
|
||||||
|
// const endTime = new Date(nowUTC);
|
||||||
|
// const startTime = new Date(lastLog.from);
|
||||||
|
// lastLog.to = nowUTC;
|
||||||
|
// lastLog.totalTime = endTime.getTime() - startTime.getTime();
|
||||||
|
// console.log(`🛑 Finalized last window: ${lastLog.title} (${lastLog.totalTime}ms)`);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Filter out logs with very short duration (less than 1 second)
|
||||||
|
const validLogs = allLogs.filter((log) => {
|
||||||
|
const duration = new Date(log.to).getTime() - new Date(log.from).getTime();
|
||||||
|
return duration >= 1000; // Minimum 1 second
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`📊 Total logs: ${allLogs.length}, Valid logs (≥1s): ${validLogs.length}`);
|
||||||
|
|
||||||
|
if (validLogs.length === 0) {
|
||||||
|
console.log("ℹ️ No valid logs to send");
|
||||||
|
dispatch(clearProjectLogs({ projectId }));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare payload with all valid logs
|
||||||
|
const payload = {
|
||||||
|
vaId: vaId || "",
|
||||||
projectId,
|
projectId,
|
||||||
lastTitleRef.current,
|
logId: activeProjectLogId,
|
||||||
lastActiveRef.current.owner.name || "Unknown",
|
logs: allLogs.map((log) => ({
|
||||||
false // PATCH (update existing log)
|
title: log.title,
|
||||||
);
|
appName: log.appName,
|
||||||
|
url: log.url ?? "",
|
||||||
|
from: log.from,
|
||||||
|
to: log.to,
|
||||||
|
totalTime: log.totalTime,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
console.log("✅ Final PATCH sent successfully!");
|
console.log("📤 Payload to send:", payload);
|
||||||
|
console.log("📊 Sending logs summary:", {
|
||||||
if (result?.dailyHours) {
|
projectId,
|
||||||
console.log("📈 Final Daily Hours:");
|
projectName,
|
||||||
console.log(` • Today: ${result.dailyHours.todayTotal}`);
|
logCount: validLogs.length,
|
||||||
console.log(` • Limit: ${result.dailyHours.dailyLimit}`);
|
totalTime: validLogs.reduce((sum, log) => sum + log.totalTime, 0),
|
||||||
console.log(` • Remaining: ${result.dailyHours.remaining}`);
|
firstLog: validLogs[0]
|
||||||
|
? {
|
||||||
|
title: validLogs[0].title.substring(0, 40),
|
||||||
|
appName: validLogs[0].appName,
|
||||||
|
duration: validLogs[0].totalTime,
|
||||||
|
from: new Date(validLogs[0].from).toLocaleTimeString(),
|
||||||
|
to: new Date(validLogs[0].to).toLocaleTimeString(),
|
||||||
}
|
}
|
||||||
|
: null,
|
||||||
} catch (error) {
|
lastLog: validLogs[validLogs.length - 1]
|
||||||
console.error("❌ Final PATCH failed:", error);
|
? {
|
||||||
throw error;
|
title: validLogs[validLogs.length - 1].title.substring(0, 40),
|
||||||
}
|
appName: validLogs[validLogs.length - 1].appName,
|
||||||
} else {
|
duration: validLogs[validLogs.length - 1].totalTime,
|
||||||
console.log("ℹ️ No active window to finalize");
|
from: new Date(validLogs[validLogs.length - 1].from).toLocaleTimeString(),
|
||||||
|
to: new Date(validLogs[validLogs.length - 1].to).toLocaleTimeString(),
|
||||||
}
|
}
|
||||||
|
: null,
|
||||||
|
});
|
||||||
|
|
||||||
// ✅ STEP 4: Clean up all refs
|
let res = window.electron.trackingTabsOpeningClosing(payload)
|
||||||
console.log("🧹 Cleaning up tracking references...");
|
|
||||||
|
console.log("Tracking response:", res);
|
||||||
|
dispatch(clearProjectLogs({ projectId }));
|
||||||
|
|
||||||
|
// Cleanup refs
|
||||||
lastActiveRef.current = null;
|
lastActiveRef.current = null;
|
||||||
lastTitleRef.current = null;
|
lastTitleRef.current = null;
|
||||||
lastLogIdRef.current = null;
|
lastAppNameRef.current = null;
|
||||||
limitCheckedRef.current = false;
|
limitCheckedRef.current = false;
|
||||||
console.log("✅ Cleanup complete");
|
|
||||||
|
console.log("✅ Logs sent and cleared successfully");
|
||||||
console.log("=".repeat(60));
|
console.log("=".repeat(60));
|
||||||
}, [activeProjectLogId, projectId, stopInterval]);
|
|
||||||
|
|
||||||
// ✅ Update ref whenever sendFinalPatch changes
|
return true;
|
||||||
useEffect(() => {
|
} catch (error) {
|
||||||
finalizeTrackingRef.current = sendFinalPatch;
|
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
||||||
}, [sendFinalPatch]);
|
console.error("❌ Error sending logs:", error);
|
||||||
|
console.log("=".repeat(60));
|
||||||
// ✅ Main tracking interval effect
|
return false;
|
||||||
useEffect(() => {
|
|
||||||
// Reset flags when tracking starts
|
|
||||||
if (isTracking) {
|
|
||||||
limitCheckedRef.current = false;
|
|
||||||
isStoppingRef.current = false;
|
|
||||||
console.log("🟢 Tracking started — flags reset");
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
activeProjectLogId,
|
||||||
|
projectId,
|
||||||
|
logsByProject,
|
||||||
|
stopIntervals,
|
||||||
|
dispatch,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
if (!isTracking || !canSend) {
|
// Main tracking logic - runs every 5 seconds
|
||||||
console.log("🟡 Tracking inactive — cleaning up");
|
useEffect(() => {
|
||||||
stopInterval();
|
if (!isTracking || !projectId || !activeProjectLogId) {
|
||||||
|
console.log("⏸️ Tracking inactive — stopping interval");
|
||||||
|
stopIntervals();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`🟢 Starting active tracker for: ${projectName}`);
|
console.log(`🚀 Starting tracker for: ${projectName}`);
|
||||||
|
|
||||||
const fetchActiveApp = async () => {
|
const checkActiveWindow = async () => {
|
||||||
// ✅ Double-check stopping flag before each fetch
|
|
||||||
if (isStoppingRef.current) {
|
if (isStoppingRef.current) {
|
||||||
console.log("🚫 Stopping in progress — skipping fetch");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const details = (await window.electron.getActiveAppDetails()) as WindowInfo;
|
const details = (await window.electron.getActiveAppDetails()) as WindowInfo;
|
||||||
if (!details || !details.id) return;
|
|
||||||
|
|
||||||
const title = details.title;
|
if (!details || !details.id || !details.owner?.name) {
|
||||||
const appName = details.owner.name;
|
return;
|
||||||
const prevTitle = lastTitleRef.current;
|
|
||||||
const hasChanged = !prevTitle || prevTitle !== title;
|
|
||||||
|
|
||||||
if (hasChanged) {
|
|
||||||
// PATCH previous window before creating new log
|
|
||||||
if (lastLogIdRef.current && prevTitle) {
|
|
||||||
console.log("🔄 Updating previous window:", prevTitle);
|
|
||||||
await sendActivityLog(activeProjectLogId!, projectId!, prevTitle, appName, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST new window log
|
const currentTitle = details.title;
|
||||||
console.log("🆕 New window detected:", title);
|
const currentAppName = details.owner.name;
|
||||||
await sendActivityLog(activeProjectLogId!, projectId!, title, appName, true);
|
|
||||||
|
|
||||||
// Update refs and Redux
|
checkCountRef.current += 1;
|
||||||
|
|
||||||
|
// Update Redux with current window activity
|
||||||
|
dispatch(
|
||||||
|
updateWindowLog({
|
||||||
|
projectId,
|
||||||
|
title: currentTitle,
|
||||||
|
appName: currentAppName,
|
||||||
|
url: null,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update refs
|
||||||
lastActiveRef.current = details;
|
lastActiveRef.current = details;
|
||||||
lastTitleRef.current = title;
|
lastTitleRef.current = currentTitle;
|
||||||
dispatch(setActiveWindow({ window: details, projectId, projectName }));
|
lastAppNameRef.current = currentAppName;
|
||||||
} else {
|
|
||||||
console.log("⏸️ Same window, skipping");
|
// Debug every 10 checks (every 50 seconds)
|
||||||
|
if (checkCountRef.current % 10 === 0) {
|
||||||
|
debugCurrentLogs();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ Failed to fetch active app:", error);
|
console.error("❌ Failed to check active window:", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initial fetch
|
// Initial check
|
||||||
fetchActiveApp();
|
checkActiveWindow();
|
||||||
|
|
||||||
// Set up interval (every 5 seconds)
|
// Set up interval (every 5 seconds)
|
||||||
intervalIdRef.current = setInterval(fetchActiveApp, 5000);
|
intervalIdRef.current = setInterval(checkActiveWindow, 5000);
|
||||||
|
|
||||||
// Cleanup on unmount
|
// Cleanup
|
||||||
return () => {
|
return () => {
|
||||||
console.log("🧹 Cleaning up tracker interval");
|
console.log("🧹 Cleaning up tracker");
|
||||||
stopInterval();
|
stopIntervals();
|
||||||
};
|
};
|
||||||
}, [isTracking, activeProjectLogId, projectId, projectName, dispatch, stopInterval]);
|
}, [
|
||||||
|
isTracking,
|
||||||
|
activeProjectLogId,
|
||||||
|
projectId,
|
||||||
|
projectName,
|
||||||
|
stopIntervals,
|
||||||
|
dispatch,
|
||||||
|
debugCurrentLogs,
|
||||||
|
]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
finalizeTracking: finalizeTrackingRef.current,
|
finalizeAndSendLogs,
|
||||||
stopInterval,
|
stopIntervals,
|
||||||
setIsStopping: (value: boolean) => {
|
setIsStopping: (value: boolean) => {
|
||||||
isStoppingRef.current = value;
|
isStoppingRef.current = value;
|
||||||
console.log(`🔒 Stopping flag manually set to: ${value}`);
|
},
|
||||||
}
|
debugCurrentLogs,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -39,6 +39,7 @@ import { canStartTracking } from "../utils/projectLimits";
|
||||||
import { useTheme } from "../helper/ThemeToggle";
|
import { useTheme } from "../helper/ThemeToggle";
|
||||||
import LogoutModal from "../modal/LogoutModal";
|
import LogoutModal from "../modal/LogoutModal";
|
||||||
import { parseTimeStringToSeconds } from "../utils/timeParser";
|
import { parseTimeStringToSeconds } from "../utils/timeParser";
|
||||||
|
import { clearAllLogs, clearProjectLogs } from "../store/logsSlice";
|
||||||
|
|
||||||
interface User {
|
interface User {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -100,6 +101,8 @@ const DashboardLayout = () => {
|
||||||
const { projectsData, projectSummary, selectedProject, projectTrackingMap } =
|
const { projectsData, projectSummary, selectedProject, projectTrackingMap } =
|
||||||
useSelector((state: any) => state.projects);
|
useSelector((state: any) => state.projects);
|
||||||
|
|
||||||
|
const { logsByProject } = useSelector((state: RootState) => state.logs);
|
||||||
|
|
||||||
const tracking = selectedProject
|
const tracking = selectedProject
|
||||||
? projectTrackingMap[selectedProject.projectId]
|
? projectTrackingMap[selectedProject.projectId]
|
||||||
: null;
|
: null;
|
||||||
|
|
@ -173,7 +176,7 @@ const DashboardLayout = () => {
|
||||||
}, [selectedProject]);
|
}, [selectedProject]);
|
||||||
|
|
||||||
// Initialize useActiveAppTracker and store finalizeTracking in ref
|
// Initialize useActiveAppTracker and store finalizeTracking in ref
|
||||||
const { finalizeTracking, setIsStopping: setIsStoppingInTracker } = useActiveAppTracker(
|
const { finalizeAndSendLogs, setIsStopping: setIsStoppingInTracker } = useActiveAppTracker(
|
||||||
isTracking,
|
isTracking,
|
||||||
activeProjectLogId,
|
activeProjectLogId,
|
||||||
selectedProject?.projectId ?? null,
|
selectedProject?.projectId ?? null,
|
||||||
|
|
@ -184,106 +187,74 @@ const DashboardLayout = () => {
|
||||||
// Define handleStopTracking FIRST
|
// Define handleStopTracking FIRST
|
||||||
const handleStopTracking = useCallback(async () => {
|
const handleStopTracking = useCallback(async () => {
|
||||||
if (loadingStop || loadingStart) {
|
if (loadingStop || loadingStart) {
|
||||||
console.log("⏸️ Already processing, skipping...");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!selectedProject) {
|
if (!selectedProject) {
|
||||||
console.warn("⚠️ No selected project to stop");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("=".repeat(70));
|
|
||||||
console.log("🛑 STOP TRACKING INITIATED");
|
console.log("🛑 STOP TRACKING INITIATED");
|
||||||
console.log("=".repeat(70));
|
|
||||||
console.log(`📦 Project: ${selectedProject.projectName}`);
|
|
||||||
console.log(`⏱️ Elapsed Time: ${elapsedTime}s`);
|
|
||||||
|
|
||||||
// Reset alert flag when stopping
|
// Set loading state
|
||||||
limitAlertShownRef.current = false;
|
setIsStopping(true);
|
||||||
|
setLoadingStop(true);
|
||||||
|
|
||||||
const currentStopTime = new Date().toISOString();
|
|
||||||
const projectId = selectedProject.projectId;
|
const projectId = selectedProject.projectId;
|
||||||
|
const vaId = projectSummary.vaId || "";
|
||||||
|
|
||||||
// ✅ CRITICAL: Set stopping flag IMMEDIATELY to block all new tab tracking
|
// STEP 1: Set stopping flag in tracker
|
||||||
console.log("🔒 Setting stopping flag...");
|
|
||||||
if (setIsStoppingInTracker) {
|
if (setIsStoppingInTracker) {
|
||||||
setIsStoppingInTracker(true);
|
setIsStoppingInTracker(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsStopping(true);
|
// STEP 2: Disconnect socket
|
||||||
setLoadingStop(true);
|
|
||||||
handleSocketDisconnect();
|
handleSocketDisconnect();
|
||||||
|
|
||||||
// STEP 1: Clear local timer to prevent further client-side updates
|
// STEP 3: Clear local timer
|
||||||
console.log("⏹️ STEP 1: Clearing local timer...");
|
|
||||||
if (timerRef.current) {
|
if (timerRef.current) {
|
||||||
clearInterval(timerRef.current);
|
clearInterval(timerRef.current);
|
||||||
timerRef.current = null;
|
timerRef.current = null;
|
||||||
console.log("✅ Timer cleared");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// STEP 2: Send final PATCH with last active window data
|
// STEP 4: Finalize and send logs
|
||||||
console.log("📤 STEP 2: Sending final activity tracking...");
|
console.log("📤 STEP 4: Finalizing and sending logs...");
|
||||||
if (stopTrackingRef.current) {
|
let logsSent = false;
|
||||||
|
if (finalizeAndSendLogs) {
|
||||||
try {
|
try {
|
||||||
await stopTrackingRef.current(); // This calls sendFinalPatch() in the hook
|
logsSent = await finalizeAndSendLogs(vaId); // Pass vaId here
|
||||||
console.log("✅ Final activity tracking completed");
|
console.log(logsSent ? "✅ Logs sent successfully" : "⚠️ Logs not sent");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ Error during final tracking:", error);
|
console.error("❌ Error sending logs:", error);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
console.log("⚠️ No tracking ref available");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// STEP 3: Send LOGOUT to backend
|
// STEP 5: Send LOGOUT to backend
|
||||||
console.log("🚪 STEP 3: Sending LOGOUT request...");
|
console.log("🚪 STEP 5: Sending LOGOUT request...");
|
||||||
try {
|
try {
|
||||||
const payload = {
|
const logoutPayload = {
|
||||||
vaId: projectSummary.vaId,
|
vaId: vaId,
|
||||||
projectId: projectId,
|
projectId: projectId,
|
||||||
type: "LOGOUT",
|
type: "LOGOUT",
|
||||||
time: currentStopTime
|
time: new Date().toISOString()
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log("📡 Logout payload:", payload);
|
await window.electron.stopProjectTracking(logoutPayload);
|
||||||
const res = await window.electron.stopProjectTracking(payload);
|
console.log("✅ LOGOUT successful");
|
||||||
console.log("✅ LOGOUT successful:", res);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ LOGOUT failed:", error);
|
console.error("❌ LOGOUT failed:", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// STEP 4: Update Redux tracking state
|
// STEP 6: Update Redux tracking state
|
||||||
console.log("🔄 STEP 4: Updating Redux state...");
|
dispatch(stopTrackingReducer({ projectId }));
|
||||||
dispatch(
|
|
||||||
stopTrackingReducer({
|
|
||||||
projectId,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// STEP 6: Update UI (restore window)
|
// STEP 7: Refresh project list
|
||||||
console.log("🪟 STEP 6: Restoring window...");
|
|
||||||
await window.electron.toggleCloseBtn(true);
|
|
||||||
await window.electron.resizeWindow(500, 600);
|
|
||||||
toggleWindowSize.current = true;
|
|
||||||
console.log("✅ Window restored");
|
|
||||||
|
|
||||||
// Allow React to process the state update
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 50));
|
|
||||||
|
|
||||||
// STEP 5: Refresh project list to get updated work hours from server
|
|
||||||
console.log("📊 STEP 5: Refreshing project list...");
|
|
||||||
try {
|
try {
|
||||||
const projectListRes = await window.electron.getProjectList();
|
const projectListRes = await window.electron.getProjectList();
|
||||||
console.log("✅ Project list fetched:", projectListRes);
|
|
||||||
|
|
||||||
if (projectListRes?.success && Array.isArray(projectListRes.data)) {
|
if (projectListRes?.success && Array.isArray(projectListRes.data)) {
|
||||||
// Update projects data
|
|
||||||
dispatch(setProjectsDataSlice(projectListRes.data));
|
dispatch(setProjectsDataSlice(projectListRes.data));
|
||||||
|
|
||||||
// Update project summary
|
dispatch(setProjectSummarySlice({
|
||||||
dispatch(
|
|
||||||
setProjectSummarySlice({
|
|
||||||
totalProjects: projectListRes.totalProjects || 0,
|
totalProjects: projectListRes.totalProjects || 0,
|
||||||
activeProjects: projectListRes.activeProjects || 0,
|
activeProjects: projectListRes.activeProjects || 0,
|
||||||
awayProjects: projectListRes.awayProjects || 0,
|
awayProjects: projectListRes.awayProjects || 0,
|
||||||
|
|
@ -291,80 +262,54 @@ const DashboardLayout = () => {
|
||||||
totalWorkMs: projectListRes.totalWorkMs || 0,
|
totalWorkMs: projectListRes.totalWorkMs || 0,
|
||||||
vaName: projectListRes.vaName || "",
|
vaName: projectListRes.vaName || "",
|
||||||
vaId: projectListRes.vaId || "",
|
vaId: projectListRes.vaId || "",
|
||||||
})
|
}));
|
||||||
);
|
|
||||||
|
|
||||||
// Update selected project with fresh data from server
|
// Update selected project
|
||||||
const updatedProject = projectListRes.data.find(
|
const updatedProject = projectListRes.data.find(
|
||||||
(p: Project) => p.projectId === projectId
|
(p: Project) => p.projectId === projectId
|
||||||
);
|
);
|
||||||
|
|
||||||
if (updatedProject) {
|
if (updatedProject) {
|
||||||
dispatch(setSelectedProjectSlice(updatedProject));
|
dispatch(setSelectedProjectSlice(updatedProject));
|
||||||
|
|
||||||
// Sync client time with server time
|
|
||||||
const serverElapsedTime = parseTimeStringToSeconds(updatedProject.totalWorkTime);
|
|
||||||
|
|
||||||
console.log("🔄 Syncing with server time:", {
|
|
||||||
serverTime: updatedProject.totalWorkTime,
|
|
||||||
serverSeconds: serverElapsedTime,
|
|
||||||
clientSeconds: elapsedTime,
|
|
||||||
difference: serverElapsedTime - elapsedTime
|
|
||||||
});
|
|
||||||
|
|
||||||
// Force update the tracking map with server time
|
|
||||||
dispatch(
|
|
||||||
updateProjectTracking({
|
|
||||||
projectId: updatedProject.projectId,
|
|
||||||
data: {
|
|
||||||
elapsedTime: serverElapsedTime,
|
|
||||||
accumulatedTime: serverElapsedTime,
|
|
||||||
isTracking: false,
|
|
||||||
isPaused: false,
|
|
||||||
startTime: null,
|
|
||||||
endTime: Date.now(),
|
|
||||||
pauseStartTime: null,
|
|
||||||
totalPausedTime: 0
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log("✅ Project synced with server");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ Error refreshing project list:", error);
|
console.error("❌ Error refreshing project list:", error);
|
||||||
} finally {
|
}
|
||||||
// Clear loading states
|
|
||||||
|
// STEP 8: Reset logs for this project
|
||||||
|
if (logsSent && projectId) {
|
||||||
|
dispatch(clearProjectLogs({ projectId }));
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP 9: Restore window
|
||||||
|
await window.electron.toggleCloseBtn(true);
|
||||||
|
await window.electron.resizeWindow(500, 600);
|
||||||
|
toggleWindowSize.current = true;
|
||||||
|
|
||||||
|
// STEP 10: Final cleanup
|
||||||
setLoadingStop(false);
|
setLoadingStop(false);
|
||||||
setIsStopping(false);
|
setIsStopping(false);
|
||||||
|
|
||||||
// ✅ Reset stopping flag for next session
|
|
||||||
if (setIsStoppingInTracker) {
|
if (setIsStoppingInTracker) {
|
||||||
setIsStoppingInTracker(false);
|
setIsStoppingInTracker(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("=".repeat(70));
|
|
||||||
console.log("🎉 TRACKING STOPPED SUCCESSFULLY");
|
console.log("🎉 TRACKING STOPPED SUCCESSFULLY");
|
||||||
console.log("=".repeat(70));
|
}, [
|
||||||
}
|
|
||||||
}, [
|
|
||||||
selectedProject,
|
selectedProject,
|
||||||
projectSummary,
|
projectSummary,
|
||||||
elapsedTime,
|
|
||||||
dispatch,
|
dispatch,
|
||||||
loadingStop,
|
loadingStop,
|
||||||
loadingStart,
|
loadingStart,
|
||||||
setIsStoppingInTracker
|
setIsStoppingInTracker,
|
||||||
]);
|
finalizeAndSendLogs,
|
||||||
|
]);
|
||||||
|
|
||||||
// Update the ref whenever finalizeTracking changes
|
// Update the ref whenever finalizeTracking changes
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
stopTrackingRef.current = finalizeTracking;
|
// stopTrackingRef.current = finalizeTracking;
|
||||||
}, [finalizeTracking]);
|
// }, [finalizeTracking]);
|
||||||
|
|
||||||
const socketRef = useRef<Socket | null>(null);
|
const socketRef = useRef<Socket | null>(null);
|
||||||
const intervalRef = useRef<number | null>(null);
|
const intervalRef = useRef<number | null>(null);
|
||||||
|
|
@ -404,6 +349,7 @@ const DashboardLayout = () => {
|
||||||
|
|
||||||
const handleLogout = async () => {
|
const handleLogout = async () => {
|
||||||
try {
|
try {
|
||||||
|
dispatch(clearAllLogs());
|
||||||
const res = await window.electron.logout();
|
const res = await window.electron.logout();
|
||||||
if (res) {
|
if (res) {
|
||||||
dispatch(clearUser());
|
dispatch(clearUser());
|
||||||
|
|
@ -459,6 +405,11 @@ const DashboardLayout = () => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset logs for this project if they exist
|
||||||
|
if (logsByProject[project.projectId]) {
|
||||||
|
dispatch(clearProjectLogs({ projectId: project.projectId }));
|
||||||
|
}
|
||||||
|
|
||||||
// CRITICAL FIX: Get the current accumulated time BEFORE starting timer
|
// CRITICAL FIX: Get the current accumulated time BEFORE starting timer
|
||||||
const currentAccumulatedTime = selectedData?.accumulatedTime || parseTimeStringToSeconds(project.totalWorkTime);
|
const currentAccumulatedTime = selectedData?.accumulatedTime || parseTimeStringToSeconds(project.totalWorkTime);
|
||||||
|
|
||||||
|
|
@ -928,3 +879,5 @@ const DashboardLayout = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DashboardLayout;
|
export default DashboardLayout;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import windowReducer from "./windowSlice";
|
||||||
import projectsReducer from "./projectSlice";
|
import projectsReducer from "./projectSlice";
|
||||||
import userReducer from "./userSlice";
|
import userReducer from "./userSlice";
|
||||||
import profileReducer from "./profileSlice";
|
import profileReducer from "./profileSlice";
|
||||||
|
import logsReducer from './logsSlice';
|
||||||
|
|
||||||
export const store = configureStore({
|
export const store = configureStore({
|
||||||
reducer: {
|
reducer: {
|
||||||
|
|
@ -11,6 +12,7 @@ export const store = configureStore({
|
||||||
projects: projectsReducer,
|
projects: projectsReducer,
|
||||||
user: userReducer,
|
user: userReducer,
|
||||||
profile: profileReducer,
|
profile: profileReducer,
|
||||||
|
logs: logsReducer,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,182 @@
|
||||||
|
// store/logsSlice.ts
|
||||||
|
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||||
|
|
||||||
|
export interface LogEntry {
|
||||||
|
id: string; // Unique ID for each log entry
|
||||||
|
title: string;
|
||||||
|
appName: string;
|
||||||
|
url: string | null;
|
||||||
|
from: string; // UTC ISO string
|
||||||
|
to: string; // UTC ISO string
|
||||||
|
totalTime: number; // milliseconds
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProjectLogs {
|
||||||
|
projectId: string;
|
||||||
|
logId: string | null;
|
||||||
|
logs: LogEntry[]; // All collected logs during tracking
|
||||||
|
trackingStartTime: string | null;
|
||||||
|
lastWindowTitle: string | null; // Track last window title
|
||||||
|
lastWindowApp: string | null; // Track last window app
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LogsState {
|
||||||
|
logsByProject: Record<string, ProjectLogs>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: LogsState = {
|
||||||
|
logsByProject: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const logsSlice = createSlice({
|
||||||
|
name: "logs",
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
// Initialize logs for a project
|
||||||
|
initializeProjectLogs: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{
|
||||||
|
projectId: string;
|
||||||
|
logId: string;
|
||||||
|
}>
|
||||||
|
) => {
|
||||||
|
const { projectId, logId } = action.payload;
|
||||||
|
const nowUTC = new Date().toISOString();
|
||||||
|
|
||||||
|
// Always create fresh logs for new tracking session
|
||||||
|
state.logsByProject[projectId] = {
|
||||||
|
projectId,
|
||||||
|
logId,
|
||||||
|
logs: [], // Start with empty logs
|
||||||
|
trackingStartTime: nowUTC,
|
||||||
|
lastWindowTitle: null,
|
||||||
|
lastWindowApp: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(`📝 Initialized logs for project ${projectId} with logId: ${logId}`);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Add or update window log
|
||||||
|
updateWindowLog: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{
|
||||||
|
projectId: string;
|
||||||
|
title: string;
|
||||||
|
appName: string;
|
||||||
|
url?: string | null;
|
||||||
|
}>
|
||||||
|
) => {
|
||||||
|
const { projectId, title, appName, url = null } = action.payload;
|
||||||
|
const projectLogs = state.logsByProject[projectId];
|
||||||
|
|
||||||
|
if (!projectLogs) {
|
||||||
|
console.warn(`Cannot update log: Project ${projectId} not initialized`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nowUTC = new Date().toISOString();
|
||||||
|
const isSameWindow =
|
||||||
|
projectLogs.lastWindowTitle === title &&
|
||||||
|
projectLogs.lastWindowApp === appName;
|
||||||
|
|
||||||
|
// If same window as before, update the last log's end time
|
||||||
|
if (isSameWindow && projectLogs.logs.length > 0) {
|
||||||
|
const lastLog = projectLogs.logs[projectLogs.logs.length - 1];
|
||||||
|
const startTime = new Date(lastLog.from);
|
||||||
|
const endTime = new Date(nowUTC);
|
||||||
|
lastLog.to = nowUTC;
|
||||||
|
lastLog.totalTime = endTime.getTime() - startTime.getTime();
|
||||||
|
// console.log(`⏱️ Updated existing log: ${title} (${lastLog.totalTime}ms)`);
|
||||||
|
}
|
||||||
|
// If new window or first window
|
||||||
|
else {
|
||||||
|
// If there was a previous window, ensure it's properly ended
|
||||||
|
if (projectLogs.lastWindowTitle && projectLogs.logs.length > 0) {
|
||||||
|
const lastLog = projectLogs.logs[projectLogs.logs.length - 1];
|
||||||
|
const endTime = new Date(nowUTC);
|
||||||
|
const startTime = new Date(lastLog.from);
|
||||||
|
lastLog.to = nowUTC;
|
||||||
|
lastLog.totalTime = endTime.getTime() - startTime.getTime();
|
||||||
|
console.log(
|
||||||
|
`✅ Finalized previous window: ${lastLog.title} (${lastLog.totalTime}ms)`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new log entry
|
||||||
|
const newLog: LogEntry = {
|
||||||
|
id: `log_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
||||||
|
title,
|
||||||
|
appName,
|
||||||
|
url,
|
||||||
|
from: nowUTC,
|
||||||
|
to: nowUTC, // Will be updated continuously
|
||||||
|
totalTime: 0, // Starts at 0
|
||||||
|
};
|
||||||
|
|
||||||
|
projectLogs.logs.push(newLog);
|
||||||
|
console.log(`📝 Started new window: ${title} on ${appName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update last window tracking
|
||||||
|
projectLogs.lastWindowTitle = title;
|
||||||
|
projectLogs.lastWindowApp = appName;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Clear project logs
|
||||||
|
clearProjectLogs: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{
|
||||||
|
projectId: string;
|
||||||
|
}>
|
||||||
|
) => {
|
||||||
|
const { projectId } = action.payload;
|
||||||
|
delete state.logsByProject[projectId];
|
||||||
|
console.log(`🧹 Cleared logs for project ${projectId}`);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Clear all logs
|
||||||
|
clearAllLogs: (state) => {
|
||||||
|
state.logsByProject = {};
|
||||||
|
console.log("🧹 Cleared all logs");
|
||||||
|
},
|
||||||
|
|
||||||
|
// Debug: Get logs state
|
||||||
|
debugLogsState: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{
|
||||||
|
projectId: string;
|
||||||
|
}>
|
||||||
|
) => {
|
||||||
|
const { projectId } = action.payload;
|
||||||
|
const projectLogs = state.logsByProject[projectId];
|
||||||
|
|
||||||
|
if (!projectLogs) {
|
||||||
|
console.log(`📋 No logs found for project ${projectId}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`📋 Logs State for ${projectId}:`, {
|
||||||
|
totalLogs: projectLogs.logs.length,
|
||||||
|
lastWindow: projectLogs.lastWindowTitle,
|
||||||
|
logs: projectLogs.logs.map((log) => ({
|
||||||
|
title:
|
||||||
|
log.title.substring(0, 40) + (log.title.length > 40 ? "..." : ""),
|
||||||
|
appName: log.appName,
|
||||||
|
from: new Date(log.from).toLocaleTimeString(),
|
||||||
|
to: new Date(log.to).toLocaleTimeString(),
|
||||||
|
duration: log.totalTime,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const {
|
||||||
|
initializeProjectLogs,
|
||||||
|
updateWindowLog,
|
||||||
|
clearProjectLogs,
|
||||||
|
clearAllLogs,
|
||||||
|
debugLogsState,
|
||||||
|
} = logsSlice.actions;
|
||||||
|
|
||||||
|
export default logsSlice.reducer;
|
||||||
|
|
@ -142,6 +142,24 @@ interface RegionResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface TrackingLog {
|
||||||
|
title: string;
|
||||||
|
appName: string;
|
||||||
|
url: string;
|
||||||
|
from: string;
|
||||||
|
to: string;
|
||||||
|
totalTime: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TrackingPayload {
|
||||||
|
vaId: string;
|
||||||
|
projectId: string;
|
||||||
|
logId: string;
|
||||||
|
logs: TrackingLog[];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
interface Window {
|
interface Window {
|
||||||
electron: {
|
electron: {
|
||||||
login: (credentials: { email: string; password: string }) => Promise<any>;
|
login: (credentials: { email: string; password: string }) => Promise<any>;
|
||||||
|
|
@ -171,14 +189,7 @@ interface Window {
|
||||||
type: string;
|
type: string;
|
||||||
time: string
|
time: string
|
||||||
}) => Promise<any>;
|
}) => Promise<any>;
|
||||||
trackingTabsOpeningClosing: (data: {
|
trackingTabsOpeningClosing: (data: TrackingPayload) => Promise<any>;
|
||||||
id?: string;
|
|
||||||
logId: string;
|
|
||||||
projectId: string;
|
|
||||||
title: string;
|
|
||||||
appName: string;
|
|
||||||
}) => Promise<any>;
|
|
||||||
|
|
||||||
getProjectLogs: (
|
getProjectLogs: (
|
||||||
projectId: string,
|
projectId: string,
|
||||||
fromDate?: string,
|
fromDate?: string,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue