'use client' import { useEffect, useState } from 'react' import { cn } from '../../../utils/cn' import { Autocomplete, type AutocompleteOption } from '../../ui/autocomplete' import { Button } from '../../ui/button/button' import { SplitButton } from '../../ui/button/split-button' import { SquareAvatar } from '../../ui/square-avatar' import { Tag } from '../../ui/tag' import { Textarea } from '../../ui/textarea' import { ArrowRightUpIcon, CheckCircleIcon, CheckIcon, Chevron02RightIcon, ClockHistoryIcon, DotsLoaderIcon, PauseIcon, PlayIcon, PlusCircleIcon, XmarkIcon, } from '../../icons-v2-generated' import { useTrackerClock } from './use-tracker-clock' import type { TimeTrackerData, TimeTrackerEntry, TimeTrackerStatus } from './types' interface CustomerAutocompleteOption extends AutocompleteOption { imageUrl?: string } function renderCustomerOption(option: AutocompleteOption, isSelected: boolean) { const { label, imageUrl } = option as CustomerAutocompleteOption return (
{label}
{isSelected && }
) } export interface TimeTrackerPanelProps extends TimeTrackerData { onClose: () => void className?: string } export function TimeTrackerPanel({ status, runningSince, accumulatedMs, ticketOptions, selectedTicketId, onSelectedTicketChange, onTicketSearch, ticketsLoading, customerOptions, selectedCustomerId, onSelectedCustomerChange, onCustomerSearch, customersLoading, customerLocked, notes, onNotesChange, lastEntries, onStart, onPause, onResume, onCancel, onSubmit, onManualEntry, onOpenMyTime, onOpenMyTimeMenu, onEntryClick, isStarting, isSubmitting, onClose, className, }: TimeTrackerPanelProps) { const elapsedLabel = useTrackerClock({ status, runningSince, accumulatedMs }) const [showValidationError, setShowValidationError] = useState(false) const isRunning = status === 'tracking' const isActive = status !== 'ready' const hasContent = selectedTicketId != null || notes.trim() !== '' const showFieldError = isActive && showValidationError && !hasContent // Reset the validation flag once a session ends so the next one starts clean. useEffect(() => { if (status === 'ready') setShowValidationError(false) }, [status]) const handleSubmit = () => { if (!hasContent) { setShowValidationError(true) return } onSubmit() } const handleManualEntry = onManualEntry ? () => { onClose() onManualEntry() } : undefined const handleEntryClick = onEntryClick ? (entry: TimeTrackerEntry) => { onClose() onEntryClick(entry) } : undefined const handleOpenMyTime = onOpenMyTime ? () => { onClose() onOpenMyTime() } : undefined const handleCancel = () => { onClose() onCancel() } const ticketAutocompleteOptions: AutocompleteOption[] = ticketOptions.map((t) => ({ label: t.label, value: t.id, })) const showCustomer = !!onSelectedCustomerChange const customerAutocompleteOptions: CustomerAutocompleteOption[] = (customerOptions ?? []).map((c) => ({ label: c.label, value: c.id, imageUrl: c.imageUrl, })) const selectedCustomer = customerAutocompleteOptions.find((o) => o.value === selectedCustomerId) const customerStartAdornment = selectedCustomer ? ( ) : undefined const visibleEntries = lastEntries.slice(0, 3) return (
{/* Status + timer + ticket assignment */}
{isActive && ( )}
{elapsedLabel}
{isActive && ( )}
{ if (reason === 'input') onTicketSearch?.(value) }} /> {showCustomer && ( { if (reason === 'input') onCustomerSearch?.(value) }} startAdornment={customerStartAdornment} renderOption={renderCustomerOption} /> )}