Files
task-manager/client/src/hooks/useTimer.ts
T
paul-nothaft 95f2b1d4a1 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
2025-09-11 22:27:05 +00:00

113 lines
3.7 KiB
TypeScript

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
};
}