Merge branch 'new-window-v2' of http://git.hicglobalsolutions.in/BhawanSingh/va-tracker - 17-Dec - 4:25

This commit is contained in:
BhagwanSingh 2025-12-17 15:25:49 +05:30
commit e3692467e3
7 changed files with 460 additions and 379 deletions

View File

@ -6,6 +6,7 @@ DNS = 1.1.1.1
[Peer]
PublicKey = jG+7H5thEiNjxY1XqI1R9oyMAMTIegLwE3tZkS1Rj2U=
AllowedIPs = 10.0.0.0/24
Endpoint = 35.95.209.161:51820
AllowedIPs = 10.0.0.0/24
PersistentKeepalive = 25

View File

@ -8,7 +8,7 @@ const httpsAgent = new https.Agent({ rejectUnauthorized: false });
export const checkRegion = async () => {
const baseURL = await getBaseURL();
console.log("checkRegion@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", baseURL)
console.log("Lin 11 BaseURL CHECK@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" ,baseURL)
try {

View File

@ -172,7 +172,7 @@ ipcMain.handle("tracking-tabs-opening-and-closing", async (event, payload) => {
const response = await api.post("/log/project/track", payload);
console.log("🟩 Log track API success:", response.data);
console.log("🟩 Log track API success:", response);
return {
success: true,

View File

@ -70,128 +70,98 @@ export const useActiveAppTracker = (
}, [projectId, dispatch]);
// Finalize and send all logs when stopping tracking
const finalizeAndSendLogs = useCallback(
// hooks/useActiveAppTracker.tsx - finalizeAndSendLogs function
const finalizeAndSendLogs = useCallback(
async (vaId: string): Promise<boolean> => {
// console.log("=".repeat(60));
console.log(" FINALIZING AND SENDING ALL LOGS");
// console.log("=".repeat(60));
console.log(" FINALIZING AND SENDING ALL LOGS");
// Set stopping flag
isStoppingRef.current = true;
// Set stopping flag
isStoppingRef.current = true;
// Stop intervals immediately
stopIntervals();
// Stop intervals immediately
stopIntervals();
if (!activeProjectLogId || !projectId) {
console.log("⚠️ Skipping — no active project");
return false;
}
const projectLogs = logsByProject[projectId];
if (!projectLogs || projectLogs.logs.length === 0) {
console.log("ℹ️ No logs found for project", projectId);
return true;
}
try {
const nowUTC = new Date().toISOString();
// Finalize the last log entry
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;
if (!activeProjectLogId || !projectId) {
console.log("⚠️ Skipping — no active project");
return false;
}
// Prepare payload with all valid logs
const payload = {
vaId: vaId || "",
projectId,
logId: activeProjectLogId,
logs: allLogs.map((log) => ({
title: log.title,
appName: log.appName,
url: log.url ?? "",
from: log.from,
to: log.to,
totalTime: log.totalTime,
})),
};
const projectLogs = logsByProject[projectId];
if (!projectLogs || projectLogs.logs.length === 0) {
console.log("ℹ️ No logs found for project", projectId);
return true;
}
console.log("📤 Payload to send:", payload);
console.log("📊 Sending logs summary:", {
projectId,
projectName,
logCount: validLogs.length,
totalTime: validLogs.reduce((sum, log) => sum + log.totalTime, 0),
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,
lastLog: validLogs[validLogs.length - 1]
? {
title: validLogs[validLogs.length - 1].title.substring(0, 40),
appName: validLogs[validLogs.length - 1].appName,
duration: validLogs[validLogs.length - 1].totalTime,
from: new Date(validLogs[validLogs.length - 1].from).toLocaleTimeString(),
to: new Date(validLogs[validLogs.length - 1].to).toLocaleTimeString(),
}
: null,
});
try {
// Create a DEEP copy of the logs
const allLogs = JSON.parse(JSON.stringify(projectLogs.logs));
let res = window.electron.trackingTabsOpeningClosing(payload)
// Filter out logs with very short duration (less than 1000ms = 1 second)
const validLogs = allLogs.filter((log:any) => {
return log.totalTime >= 1000; // Minimum 1 second (1000ms)
});
console.log("Tracking response:", res);
dispatch(clearProjectLogs({ projectId }));
console.log(`📊 Total logs: ${allLogs.length}, Valid logs (≥1000ms): ${validLogs.length}`);
// Cleanup refs
lastActiveRef.current = null;
lastTitleRef.current = null;
lastAppNameRef.current = null;
limitCheckedRef.current = false;
if (validLogs.length === 0) {
console.log("ℹ️ No valid logs to send");
dispatch(clearProjectLogs({ projectId }));
return true;
}
console.log("✅ Logs sent and cleared successfully");
console.log("=".repeat(60));
// Prepare payload with all valid logs - send directly as stored
const payload = {
vaId: vaId || "",
projectId,
logId: activeProjectLogId,
logs: validLogs, // Already formatted correctly
};
return true;
} catch (error) {
const errorMsg = error instanceof Error ? error.message : "Unknown error";
console.error("❌ Error sending logs:", error);
console.log("=".repeat(60));
return false;
}
console.log("📤 Payload to send:", payload);
// console.log("📊 Sending logs summary:", {
// projectId,
// projectName,
// logCount: validLogs.length,
// totalTimeMs: validLogs.reduce((sum, log) => sum + log.totalTime, 0),
// totalTimeSec: validLogs.reduce((sum, log) => sum + Math.round(log.totalTime / 1000), 0),
// sampleLog: validLogs[0] ? {
// title: validLogs[0].title.substring(0, 40),
// from: validLogs[0].from,
// to: validLogs[0].to,
// durationMs: validLogs[0].totalTime,
// } : null,
// });
let res = window.electron.trackingTabsOpeningClosing(payload);
console.log("Tracking response:", res);
dispatch(clearProjectLogs({ projectId }));
// Cleanup refs
lastActiveRef.current = null;
lastTitleRef.current = null;
lastAppNameRef.current = null;
limitCheckedRef.current = false;
console.log("✅ Logs sent and cleared successfully");
console.log("=".repeat(60));
return true;
} catch (error) {
const errorMsg = error instanceof Error ? error.message : "Unknown error";
console.error("❌ Error sending logs:", error);
console.log("=".repeat(60));
return false;
}
},
[
activeProjectLogId,
projectId,
logsByProject,
stopIntervals,
dispatch,
activeProjectLogId,
projectId,
logsByProject,
stopIntervals,
dispatch,
]
);
);
// Main tracking logic - runs every 5 seconds
useEffect(() => {

View File

@ -101,7 +101,7 @@ const DashboardLayout = () => {
const { projectsData, projectSummary, selectedProject, projectTrackingMap } =
useSelector((state: any) => state.projects);
const { logsByProject } = useSelector((state: RootState) => state.logs);
const { logsByProject } = useSelector((state: RootState) => state.logs);
const tracking = selectedProject
? projectTrackingMap[selectedProject.projectId]
@ -176,7 +176,7 @@ const DashboardLayout = () => {
}, [selectedProject]);
// Initialize useActiveAppTracker and store finalizeTracking in ref
const { finalizeAndSendLogs, setIsStopping: setIsStoppingInTracker } = useActiveAppTracker(
const { finalizeAndSendLogs, setIsStopping: setIsStoppingInTracker } = useActiveAppTracker(
isTracking,
activeProjectLogId,
selectedProject?.projectId ?? null,
@ -185,126 +185,177 @@ const DashboardLayout = () => {
);
// Define handleStopTracking FIRST
const handleStopTracking = useCallback(async () => {
if (loadingStop || loadingStart) {
return;
}
if (!selectedProject) {
return;
}
console.log("🛑 STOP TRACKING INITIATED");
// Set loading state
setIsStopping(true);
setLoadingStop(true);
const projectId = selectedProject.projectId;
const vaId = projectSummary.vaId || "";
// STEP 1: Set stopping flag in tracker
if (setIsStoppingInTracker) {
setIsStoppingInTracker(true);
}
// STEP 2: Disconnect socket
handleSocketDisconnect();
// STEP 3: Clear local timer
if (timerRef.current) {
clearInterval(timerRef.current);
timerRef.current = null;
}
// STEP 4: Finalize and send logs
console.log("📤 STEP 4: Finalizing and sending logs...");
let logsSent = false;
if (finalizeAndSendLogs) {
try {
logsSent = await finalizeAndSendLogs(vaId); // Pass vaId here
console.log(logsSent ? "✅ Logs sent successfully" : "⚠️ Logs not sent");
} catch (error) {
console.error("❌ Error sending logs:", error);
const handleStopTracking = useCallback(async () => {
if (loadingStop || loadingStart) {
return;
}
}
// STEP 5: Send LOGOUT to backend
console.log("🚪 STEP 5: Sending LOGOUT request...");
try {
const logoutPayload = {
vaId: vaId,
projectId: projectId,
type: "LOGOUT",
time: new Date().toISOString()
};
if (!selectedProject) {
return;
}
await window.electron.stopProjectTracking(logoutPayload);
console.log("✅ LOGOUT successful");
} catch (error) {
console.error("❌ LOGOUT failed:", error);
}
console.log("🛑 STOP TRACKING INITIATED");
// STEP 6: Update Redux tracking state
dispatch(stopTrackingReducer({ projectId }));
// Reset alert flag when stopping
limitAlertShownRef.current = false;
// STEP 7: Refresh project list
try {
const projectListRes = await window.electron.getProjectList();
if (projectListRes?.success && Array.isArray(projectListRes.data)) {
dispatch(setProjectsDataSlice(projectListRes.data));
dispatch(setProjectSummarySlice({
totalProjects: projectListRes.totalProjects || 0,
activeProjects: projectListRes.activeProjects || 0,
awayProjects: projectListRes.awayProjects || 0,
totalWorkTime: projectListRes.totalWorkTime || "0h 0m 0s",
totalWorkMs: projectListRes.totalWorkMs || 0,
vaName: projectListRes.vaName || "",
vaId: projectListRes.vaId || "",
}));
const currentStopTime = new Date().toISOString();
const projectId = selectedProject.projectId;
const vaId = projectSummary.vaId || "";
// Update selected project
const updatedProject = projectListRes.data.find(
(p: Project) => p.projectId === projectId
);
// STEP 1: Set stopping flag in tracker
if (setIsStoppingInTracker) {
setIsStoppingInTracker(true);
}
if (updatedProject) {
dispatch(setSelectedProjectSlice(updatedProject));
// Set loading state
setIsStopping(true);
setLoadingStop(true);
// STEP 2: Disconnect socket
handleSocketDisconnect();
// STEP 3: Clear local timer
if (timerRef.current) {
clearInterval(timerRef.current);
timerRef.current = null;
}
// STEP 4: Finalize and send logs
console.log("📤 STEP 4: Finalizing and sending logs...");
let logsSent = false;
if (finalizeAndSendLogs) {
try {
logsSent = await finalizeAndSendLogs(vaId); // Pass vaId here
console.log(logsSent ? "✅ Logs sent successfully" : "⚠️ Logs not sent");
} catch (error) {
console.error("❌ Error sending logs:", error);
}
}
} catch (error) {
console.error("❌ Error refreshing project list:", error);
}
// STEP 8: Reset logs for this project
if (logsSent && projectId) {
dispatch(clearProjectLogs({ projectId }));
}
// STEP 5: Send LOGOUT to backend
console.log("🚪 STEP 5: Sending LOGOUT request...");
try {
const logoutPayload = {
vaId: vaId,
logId: activeProjectLogId,
projectId: projectId,
type: "LOGOUT",
time: currentStopTime
};
// STEP 9: Restore window
await window.electron.toggleCloseBtn(true);
await window.electron.resizeWindow(500, 600);
toggleWindowSize.current = true;
await window.electron.stopProjectTracking(logoutPayload);
console.log("✅ LOGOUT successful");
} catch (error) {
console.error("❌ LOGOUT failed:", error);
}
// STEP 10: Final cleanup
setLoadingStop(false);
setIsStopping(false);
// STEP 6: Update Redux tracking state
dispatch(stopTrackingReducer({ projectId }));
if (setIsStoppingInTracker) {
setIsStoppingInTracker(false);
}
await window.electron.toggleCloseBtn(true);
await window.electron.resizeWindow(500, 600);
toggleWindowSize.current = true;
console.log("🎉 TRACKING STOPPED SUCCESSFULLY");
}, [
selectedProject,
projectSummary,
dispatch,
loadingStop,
loadingStart,
setIsStoppingInTracker,
finalizeAndSendLogs,
]);
// Allow React to process the state update
await new Promise(resolve => setTimeout(resolve, 50));
// STEP 7: Refresh project list
try {
const projectListRes = await window.electron.getProjectList();
console.log("✅ Project list fetched:", projectListRes);
if (projectListRes?.success && Array.isArray(projectListRes.data)) {
// Update projects data
dispatch(setProjectsDataSlice(projectListRes.data));
// Update project summary
dispatch(
setProjectSummarySlice({
totalProjects: projectListRes.totalProjects || 0,
activeProjects: projectListRes.activeProjects || 0,
awayProjects: projectListRes.awayProjects || 0,
totalWorkTime: projectListRes.totalWorkTime || "0h 0m 0s",
totalWorkMs: projectListRes.totalWorkMs || 0,
vaName: projectListRes.vaName || "",
vaId: projectListRes.vaId || "",
})
);
// Update selected project with fresh data from server
const updatedProject = projectListRes.data.find(
(p: Project) => p.projectId === projectId
);
if (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) {
console.error("❌ Error refreshing project list:", error);
} finally {
// Clear loading states
setLoadingStop(false);
setIsStopping(false);
// ✅ Reset stopping flag for next session
if (setIsStoppingInTracker) {
setIsStoppingInTracker(false);
}
console.log("=".repeat(70));
console.log("🎉 TRACKING STOPPED SUCCESSFULLY");
console.log("=".repeat(70));
}
// STEP 8: Reset logs for this project
if (logsSent && projectId) {
dispatch(clearProjectLogs({ projectId }));
}
console.log("🎉 TRACKING STOPPED SUCCESSFULLY");
}, [
selectedProject,
projectSummary,
dispatch,
loadingStop,
loadingStart,
setIsStoppingInTracker,
finalizeAndSendLogs,
]);
// Update the ref whenever finalizeTracking changes
// useEffect(() => {
@ -406,9 +457,9 @@ const DashboardLayout = () => {
}
// Reset logs for this project if they exist
if (logsByProject[project.projectId]) {
dispatch(clearProjectLogs({ projectId: project.projectId }));
}
if (logsByProject[project.projectId]) {
dispatch(clearProjectLogs({ projectId: project.projectId }));
}
// CRITICAL FIX: Get the current accumulated time BEFORE starting timer
const currentAccumulatedTime = selectedData?.accumulatedTime || parseTimeStringToSeconds(project.totalWorkTime);

View File

@ -2,181 +2,239 @@
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
id: string; // Unique ID for each log entry
title: string;
appName: string;
url: string | null;
from: string; // UTC ISO string with .000 milliseconds
to: string; // UTC ISO string with .000 milliseconds
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
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>;
logsByProject: Record<string, ProjectLogs>;
}
const initialState: LogsState = {
logsByProject: {},
logsByProject: {},
};
// Helper function to get timestamp with .000 milliseconds
const getTimestampWithZeroMs = () => {
const now = new Date();
// Format: YYYY-MM-DDTHH:mm:ss.000Z
const isoString = now.toISOString();
// Replace milliseconds with .000
return isoString.replace(/\.\d{3}Z$/, '.000Z');
};
// Helper function to format any date string to have .000 milliseconds
const formatToZeroMs = (dateString: string): string => {
if (dateString.endsWith('.000Z')) {
return dateString; // Already formatted
}
const date = new Date(dateString);
const isoString = date.toISOString();
return isoString.replace(/\.\d{3}Z$/, '.000Z');
};
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();
name: "logs",
initialState,
reducers: {
// Initialize logs for a project
initializeProjectLogs: (
state,
action: PayloadAction<{
projectId: string;
logId: string;
}>
) => {
const { projectId, logId } = action.payload;
const nowUTC = getTimestampWithZeroMs();
// Always create fresh logs for new tracking session
state.logsByProject[projectId] = {
projectId,
logId,
logs: [], // Start with empty logs
trackingStartTime: nowUTC,
lastWindowTitle: null,
lastWindowApp: null,
};
// 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}`);
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 = getTimestampWithZeroMs();
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];
// Format the existing from time to ensure it has .000 milliseconds
lastLog.from = formatToZeroMs(lastLog.from);
lastLog.to = nowUTC;
// Calculate total time in milliseconds
const startTime = new Date(lastLog.from).getTime();
const endTime = new Date(lastLog.to).getTime();
lastLog.totalTime = endTime - startTime;
// 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];
// Format the existing from time to ensure it has .000 milliseconds
lastLog.from = formatToZeroMs(lastLog.from);
lastLog.to = nowUTC;
// Calculate total time in milliseconds
const startTime = new Date(lastLog.from).getTime();
const endTime = new Date(lastLog.to).getTime();
lastLog.totalTime = endTime - startTime;
console.log(
`✅ Finalized previous window: ${lastLog.title} (${lastLog.totalTime}ms)`
);
}
// Create new log entry with .000 milliseconds
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 (in milliseconds)
};
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: log.from,
to: log.to,
durationMs: log.totalTime,
durationSec: Math.round(log.totalTime / 1000),
})),
});
},
// Optional: Add a reducer to fix all timestamps in existing logs
fixAllTimestamps: (
state,
action: PayloadAction<{
projectId: string;
}>
) => {
const { projectId } = action.payload;
const projectLogs = state.logsByProject[projectId];
if (!projectLogs) {
console.warn(`Cannot fix timestamps: Project ${projectId} not found`);
return;
}
// Fix all timestamps in existing logs
projectLogs.logs.forEach(log => {
log.from = formatToZeroMs(log.from);
log.to = formatToZeroMs(log.to);
// Recalculate totalTime based on formatted timestamps
const startTime = new Date(log.from).getTime();
const endTime = new Date(log.to).getTime();
log.totalTime = endTime - startTime;
});
console.log(`🔧 Fixed timestamps for project ${projectId}`);
},
},
// 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,
initializeProjectLogs,
updateWindowLog,
clearProjectLogs,
clearAllLogs,
debugLogsState,
fixAllTimestamps,
} = logsSlice.actions;
export default logsSlice.reducer;

1
types.d.ts vendored
View File

@ -185,6 +185,7 @@ interface Window {
}) => Promise<any>;
stopProjectTracking: (data: {
vaId: string;
logId: string,
projectId: string;
type: string;
time: string