nyuchimzizi
Mzizi — an open-architecture project of the Bundu Foundation, operated and developed by Nyuchi. Built on the Five African Minerals palette.
Built by Nyuchi Africav4.0.39
Elapsed time display with start/stop/pause controls. Shows running timer, accumulated time, and optional description. Used for time tracking on tasks, billable hours in Nyuchi Tools, and focus sessions.
View the full component source code below.
"use client"
import * as React from "react"
import { cn } from "@/lib/utils"
interface TimeTrackerProps extends React.ComponentProps<"div"> {
/** Initial elapsed seconds */
initialSeconds?: number
/** Called when timer state changes */
onStateChange?: (state: "running" | "paused" | "stopped", elapsed: number) => void
/** Label for what is being tracked */
label?: string
/** Compact inline mode */
compact?: boolean
}
function formatElapsed(seconds: number): string {
const h = Math.floor(seconds / 3600)
const m = Math.floor((seconds % 3600) / 60)
const s = seconds % 60
if (h > 0) return `${h}:${m.toString().padStart(2, "0")}:${s.toString().padStart(2, "0")}`
return `${m}:${s.toString().padStart(2, "0")}`
}
function TimeTracker({ initialSeconds = 0, onStateChange, label, compact = false, loading = false, className, ...props }: TimeTrackerProps) {
const [elapsed, setElapsed] = React.useState(initialSeconds)
const [state, setState] = React.useState<"running" | "paused" | "stopped">("stopped")
const intervalRef = React.useRef<ReturnType<typeof setInterval> | null>(null)
React.useEffect(() => {
if (state === "running") {
intervalRef.current = setInterval(() => setElapsed(e => e + 1), 1000)
} else if (intervalRef.current) {
clearInterval(intervalRef.current)
}
return () => { if (intervalRef.current) clearInterval(intervalRef.current) }
}, [state])
const toggle = () => {
const next = state === "running" ? "paused" : "running"
setState(next)
onStateChange?.(next, elapsed)
}
const stop = () => {
setState("stopped")
onStateChange?.("stopped", elapsed)
setElapsed(0)
}
return (
<div
data-slot="time-tracker" data-portal="https://design.nyuchi.com/components/time-tracker" role="timer" aria-label="Time tracker"
className={cn(compact ? "inline-flex items-center gap-2" : "flex items-center gap-3 rounded-[var(--radius-lg,14px)] border border-border bg-card px-4 py-3", className)}
{...props}
>
{label && !compact && <span className="text-xs text-muted-foreground">{label}</span>}
<span className={cn("font-mono tabular-nums", compact ? "text-sm" : "text-lg font-bold")}>
{formatElapsed(elapsed)}
</span>
<div className="flex gap-1">
<button
onClick={toggle}
className={cn("rounded-full px-3 text-xs font-medium", compact ? "h-7" : "h-8",
state === "running" ? "bg-[var(--color-gold,#FFD740)]/15 text-[var(--color-gold,#FFD740)]" : "bg-[var(--color-malachite,#64FFDA)]/15 text-[var(--color-malachite,#64FFDA)]"
)}
>
{state === "running" ? "Pause" : "Start"}
</button>
{(state === "running" || state === "paused") && (
<button onClick={stop} className={cn("rounded-full bg-muted px-3 text-xs font-medium text-muted-foreground", compact ? "h-7" : "h-8")}>
Stop
</button>
)}
</div>
</div>
)
}
export { TimeTracker }
export type { TimeTrackerProps }
npx shadcn@latest add https://mzizi.dev/api/v1/ui/time-trackerFetch this component's metadata and source code from the registry API.
/api/v1/ui/time-tracker