| 1 | 'use client' |
| 2 | |
| 3 | import { useRouter } from '@bprogress/next/app' |
| 4 | import { Check, Pause |
| 5 | import { usePathname } |
| 6 | import { useEffect, useState |
| 7 | import { Button } |
| 8 | import { |
| 9 | elapsedMinutesFromMs, |
| 10 | formatElapsedMs, |
| 11 | getElapsedMs, |
| 12 | useTimerStore, |
| 13 | } from '@/stores/timer-store' |
| 14 | |
| 15 | export function FloatingTimer() |
| 16 | const { startedAt, |
| 17 | useTimerStore((s) |
| 18 | const router = |
| 19 | const pathname = |
| 20 | const [now, |
| 21 | |
| 22 | const isActive = |
| 23 | const isRunning = |
| 24 | |
| 25 | useEffect(() => |
| 26 | if (!isRunning) |
| 27 | return |
| 28 | } |
| 29 | const id = |
| 30 | return () => |
| 31 | }, [isRunning]) |
| 32 | |
| 33 | if (!(isActive |
| 34 | return null |
| 35 | } |
| 36 | |
| 37 | const elapsedMs = |
| 38 | const timesheetsPath = |
| 39 | const isOnTimesheetsPage = |
| 40 | |
| 41 | const handleLog = |
| 42 | const minutes = |
| 43 | stop() |
| 44 | if (isOnTimesheetsPage) { |
| 45 | window.dispatchEvent( |
| 46 | new CustomEvent('timer:log |
| 47 | ) |
| 48 | } else { |
| 49 | router.push(`${ |
| 50 | } |
| 51 | } |
| 52 | |
| 53 | return ( |
| 54 | <div className='fixed right-4 bottom-4 z-50 flex flex-col gap-3 rounded-xl border border-border/70 bg-card/95 p-3 shadow-xl backdrop-blur |
| 55 | <div className='flex items-center gap-2 text-muted-foreground text-xs |
| 56 | {isRunning ? ( |
| 57 | <span className='relative flex size-1.5 |
| 58 | <span className='absolute inline-flex h-full w-full animate-ping rounded-full bg-primary/60 opacity-75 |
| 59 | <span className='relative inline-flex size-1.5 rounded-full bg-primary |
| 60 | </span> |
| 61 | ) : ( |
| 62 | <span className='inline-flex size-1.5 rounded-full bg-muted-foreground/50 |
| 63 | )} |
| 64 | <span className='font-mono uppercase tracking-wider |
| 65 | {isRunning ? 'Recording |
| 66 | </span> |
| 67 | <span className='text-muted-foreground/50 |
| 68 | <span className='max-w-[12rem] truncate font-mono text-muted-foreground/80 |
| 69 | {context.projectName} |
| 70 | </span> |
| 71 | </div> |
| 72 | <div className='font-semibold text-3xl text-foreground tabular-nums tracking-[-0.04em] |
| 73 | {formatElapsedMs(elapsedMs)} |
| 74 | </div> |
| 75 | <div className='flex items-center gap-2 |
| 76 | {isRunning ? ( |
| 77 | <Button |
| 78 | className='flex-1' |
| 79 | onClick={pause} |
| 80 | size='sm' |
| 81 | variant='outline' |
| 82 | > |
| 83 | <Pause className=' |
| 84 | Pause |
| 85 | </Button> |
| 86 | ) : ( |
| 87 | <> |
| 88 | <Button |
| 89 | className='flex-1' |
| 90 | onClick={resume} |
| 91 | size='sm' |
| 92 | variant='outline' |
| 93 | > |
| 94 | <Play className=' |
| 95 | Resume |
| 96 | </Button> |
| 97 | <Button className=' |
| 98 | <Check className=' |
| 99 | Log time |
| 100 | </Button> |
| 101 | </> |
| 102 | )} |
| 103 | </div> |
| 104 | </div> |
| 105 | ) |
| 106 | } |