From 30caa3112b95237618067a6edcf80013b5e88afe Mon Sep 17 00:00:00 2001 From: Cody Seibert Date: Tue, 9 Dec 2025 01:49:54 -0500 Subject: [PATCH] feat(kanban): Add count-up timer component for in-progress cards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Create CountUpTimer component that displays elapsed time in MM:SS format - Timer starts counting from when a card moves to in_progress status - Shows timer on running cards (purple) and idle in_progress cards (yellow) - Updates every second to show current elapsed time 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- app/src/components/ui/count-up-timer.tsx | 63 ++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 app/src/components/ui/count-up-timer.tsx diff --git a/app/src/components/ui/count-up-timer.tsx b/app/src/components/ui/count-up-timer.tsx new file mode 100644 index 00000000..70514f94 --- /dev/null +++ b/app/src/components/ui/count-up-timer.tsx @@ -0,0 +1,63 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { Clock } from "lucide-react"; + +interface CountUpTimerProps { + startedAt: string; // ISO timestamp string + className?: string; +} + +/** + * Formats elapsed time in MM:SS format + * @param seconds - Total elapsed seconds + * @returns Formatted string like "00:00", "01:30", "59:59", etc. + */ +function formatElapsedTime(seconds: number): string { + const minutes = Math.floor(seconds / 60); + const remainingSeconds = seconds % 60; + + const paddedMinutes = minutes.toString().padStart(2, "0"); + const paddedSeconds = remainingSeconds.toString().padStart(2, "0"); + + return `${paddedMinutes}:${paddedSeconds}`; +} + +/** + * CountUpTimer component that displays elapsed time since a given start time + * Updates every second to show the current elapsed time in MM:SS format + */ +export function CountUpTimer({ startedAt, className = "" }: CountUpTimerProps) { + const [elapsedSeconds, setElapsedSeconds] = useState(0); + + useEffect(() => { + // Calculate initial elapsed time + const startTime = new Date(startedAt).getTime(); + + const calculateElapsed = () => { + const now = Date.now(); + const elapsed = Math.floor((now - startTime) / 1000); + return Math.max(0, elapsed); // Ensure non-negative + }; + + // Set initial value + setElapsedSeconds(calculateElapsed()); + + // Update every second + const interval = setInterval(() => { + setElapsedSeconds(calculateElapsed()); + }, 1000); + + return () => clearInterval(interval); + }, [startedAt]); + + return ( +
+ + {formatElapsedTime(elapsedSeconds)} +
+ ); +}