Add timer functionality to track time spent on tasks
Introduces a `useTimer` hook to manage starting and stopping timers for tasks, updating their `timeTracked` property, and displaying tracking status. This involves changes in `App.tsx`, `KanbanBoard.tsx`, `TaskCard.tsx`, and `TasksWithCalendar.tsx` to integrate the timer start/stop actions and display. Replit-Commit-Author: Agent Replit-Commit-Session-Id: ceced2fc-aa46-458d-ba87-ddd4b7bb1518 Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/659922a9-0087-461c-90dd-6d9a58b81d4d/ceced2fc-aa46-458d-ba87-ddd4b7bb1518/P2PZNJ9
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { Task } from '@shared/schema';
|
||||
|
||||
interface UseTimerProps {
|
||||
tasks: Task[];
|
||||
onTaskUpdate: (taskId: string, updates: Partial<Task>) => void;
|
||||
}
|
||||
|
||||
export function useTimer({ tasks, onTaskUpdate }: UseTimerProps) {
|
||||
const intervalRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const lastUpdateRef = useRef<{ [taskId: string]: number }>({});
|
||||
|
||||
useEffect(() => {
|
||||
// Find tasks that are currently being tracked
|
||||
const trackingTasks = tasks.filter(task => task.isTracking);
|
||||
|
||||
if (trackingTasks.length > 0) {
|
||||
// Start the timer
|
||||
intervalRef.current = setInterval(() => {
|
||||
const now = Date.now();
|
||||
|
||||
trackingTasks.forEach(task => {
|
||||
// Initialize last update time if not set
|
||||
if (!lastUpdateRef.current[task.id]) {
|
||||
lastUpdateRef.current[task.id] = now;
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate elapsed time since last update (in minutes)
|
||||
const elapsed = (now - lastUpdateRef.current[task.id]) / 1000 / 60;
|
||||
|
||||
// Only update if at least 1 minute has passed (to avoid too frequent updates)
|
||||
if (elapsed >= 1) {
|
||||
const currentTime = task.timeTracked || 0;
|
||||
const newTime = currentTime + Math.floor(elapsed);
|
||||
|
||||
onTaskUpdate(task.id, { timeTracked: newTime });
|
||||
lastUpdateRef.current[task.id] = now;
|
||||
|
||||
console.log(`Timer updated for "${task.title}": +${Math.floor(elapsed)}min (total: ${newTime}min)`);
|
||||
}
|
||||
});
|
||||
}, 60000); // Update every minute
|
||||
} else {
|
||||
// No tasks are being tracked, clear the timer
|
||||
if (intervalRef.current) {
|
||||
clearInterval(intervalRef.current);
|
||||
intervalRef.current = null;
|
||||
}
|
||||
// Clear last update times for stopped tasks
|
||||
lastUpdateRef.current = {};
|
||||
}
|
||||
|
||||
// Cleanup on unmount or when tracking tasks change
|
||||
return () => {
|
||||
if (intervalRef.current) {
|
||||
clearInterval(intervalRef.current);
|
||||
intervalRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [tasks.map(t => `${t.id}:${t.isTracking}`).join(','), onTaskUpdate]);
|
||||
|
||||
// Initialize last update times for currently tracking tasks
|
||||
useEffect(() => {
|
||||
const now = Date.now();
|
||||
tasks.forEach(task => {
|
||||
if (task.isTracking && !lastUpdateRef.current[task.id]) {
|
||||
lastUpdateRef.current[task.id] = now;
|
||||
} else if (!task.isTracking && lastUpdateRef.current[task.id]) {
|
||||
delete lastUpdateRef.current[task.id];
|
||||
}
|
||||
});
|
||||
}, [tasks]);
|
||||
|
||||
const startTimer = (taskId: string) => {
|
||||
const now = Date.now();
|
||||
lastUpdateRef.current[taskId] = now;
|
||||
onTaskUpdate(taskId, { isTracking: true });
|
||||
console.log(`Timer started for task: ${taskId}`);
|
||||
};
|
||||
|
||||
const stopTimer = (taskId: string) => {
|
||||
const task = tasks.find(t => t.id === taskId);
|
||||
if (task && lastUpdateRef.current[taskId]) {
|
||||
const now = Date.now();
|
||||
const elapsed = (now - lastUpdateRef.current[taskId]) / 1000 / 60;
|
||||
|
||||
if (elapsed >= 0.1) { // Add time if at least 6 seconds have passed
|
||||
const currentTime = task.timeTracked || 0;
|
||||
const additionalTime = Math.max(1, Math.round(elapsed)); // At least 1 minute
|
||||
const newTime = currentTime + additionalTime;
|
||||
|
||||
onTaskUpdate(taskId, {
|
||||
isTracking: false,
|
||||
timeTracked: newTime
|
||||
});
|
||||
|
||||
console.log(`Timer stopped for "${task.title}": +${additionalTime}min (total: ${newTime}min)`);
|
||||
} else {
|
||||
onTaskUpdate(taskId, { isTracking: false });
|
||||
}
|
||||
|
||||
delete lastUpdateRef.current[taskId];
|
||||
} else {
|
||||
onTaskUpdate(taskId, { isTracking: false });
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
startTimer,
|
||||
stopTimer
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user