"use client"; import React, { useState, useEffect, useRef, useCallback } from "react"; /* ------------------------------------------------------------------ */ /* Types */ /* ------------------------------------------------------------------ */ export type TimerStatus = "idle" | "running" | "paused" | "completed"; interface ProcessTimerProps { status: TimerStatus; /** ISO string or epoch — when the timer was first started */ startedAt: string | null; /** ISO string or epoch — when paused (null if not paused) */ pausedAt: string | null; /** Total paused seconds accumulated before current pause */ totalPausedSeconds: number; /** Completed at timestamp */ completedAt: string | null; /** Actual work time in seconds (from server, used when completed) */ actualWorkTime: number | null; onStart: () => void; onPause: () => void; onResume: () => void; onComplete: () => void; disabled?: boolean; } /* ------------------------------------------------------------------ */ /* Helpers */ /* ------------------------------------------------------------------ */ function formatTime(totalSeconds: number): string { const h = Math.floor(totalSeconds / 3600); const m = Math.floor((totalSeconds % 3600) / 60); const s = totalSeconds % 60; return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`; } /* ------------------------------------------------------------------ */ /* Component */ /* ------------------------------------------------------------------ */ export function ProcessTimer({ status, startedAt, pausedAt, totalPausedSeconds, completedAt, actualWorkTime, onStart, onPause, onResume, onComplete, disabled = false, }: ProcessTimerProps) { const [elapsed, setElapsed] = useState(0); const intervalRef = useRef(null); const calcElapsed = useCallback(() => { if (!startedAt) return 0; const start = new Date(startedAt).getTime(); const now = Date.now(); let pausedMs = totalPausedSeconds * 1000; // If currently paused, add time since pause started if (pausedAt) { const pauseStart = new Date(pausedAt).getTime(); pausedMs += now - pauseStart; } return Math.max(0, Math.floor((now - start - pausedMs) / 1000)); }, [startedAt, pausedAt, totalPausedSeconds]); useEffect(() => { if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null; } if (status === "completed" && actualWorkTime !== null) { setElapsed(actualWorkTime); return; } if (status === "running") { setElapsed(calcElapsed()); intervalRef.current = setInterval(() => { setElapsed(calcElapsed()); }, 1000); } else if (status === "paused") { setElapsed(calcElapsed()); } else if (status === "idle") { setElapsed(0); } return () => { if (intervalRef.current) { clearInterval(intervalRef.current); } }; }, [status, startedAt, pausedAt, totalPausedSeconds, actualWorkTime, calcElapsed]); /* Color by status */ const colorMap: Record = { idle: { bg: "bg-gray-50", text: "text-gray-400", border: "border-gray-200", ring: "ring-gray-200" }, running: { bg: "bg-blue-50", text: "text-blue-600", border: "border-blue-200", ring: "ring-blue-300" }, paused: { bg: "bg-amber-50", text: "text-amber-600", border: "border-amber-200", ring: "ring-amber-300" }, completed: { bg: "bg-green-50", text: "text-green-600", border: "border-green-200", ring: "ring-green-300" }, }; const colors = colorMap[status]; const statusLabels: Record = { idle: "대기", running: "진행중", paused: "일시정지", completed: "완료", }; return (
{/* Status badge */}
{statusLabels[status]} {status === "running" && ( 작업중 )}
{/* Timer display */}

{formatTime(elapsed)}

{startedAt && (

시작: {new Date(startedAt).toLocaleTimeString("ko-KR")} {completedAt && ` | 종료: ${new Date(completedAt).toLocaleTimeString("ko-KR")}`}

)}
{/* Buttons */}
{status === "idle" && ( )} {status === "running" && ( <> )} {status === "paused" && ( <> )} {status === "completed" && (
작업 완료
)}
); }