import { DEFAULT_CLOCK_DESCRIPTION, LEGACY_DEFAULT_CLOCK_DESCRIPTION, } from "@beenvoice/domain/time-clock"; import type { ClockOutOutcome } from "@beenvoice/domain/time-clock"; export { DEFAULT_CLOCK_DESCRIPTION, formatElapsedHoursMinutes, formatElapsedSeconds, LEGACY_DEFAULT_CLOCK_DESCRIPTION, type ClockOutOutcome, } from "@beenvoice/domain/time-clock"; export function resolveClockDescription(description: string | null | undefined): string { const trimmed = description?.trim(); return trimmed || DEFAULT_CLOCK_DESCRIPTION; } export function formatRunningTimerLabel(description?: string | null): string { const trimmed = description?.trim() ?? ""; if ( !trimmed || trimmed === DEFAULT_CLOCK_DESCRIPTION || trimmed === LEGACY_DEFAULT_CLOCK_DESCRIPTION ) { return "Clocked in"; } return trimmed; } export function resolveEffectiveHourlyRate( rateText: string, clientDefaultRate?: number | null, ): number | null { const parsed = rateText.trim() ? Number(rateText) : null; if (parsed != null && !Number.isNaN(parsed) && parsed >= 0) return parsed; if (clientDefaultRate != null && clientDefaultRate >= 0) return clientDefaultRate; return null; } export function startedAtFromMinutesAgo(minutes: number): Date { return new Date(Date.now() - minutes * 60_000); } export function describeClockOutOutcome(input: { outcome: ClockOutOutcome; hours: number; rate: number; invoice?: { invoicePrefix: string; invoiceNumber: string } | null; }): string { const amount = input.hours * input.rate; switch (input.outcome) { case "linked_to_invoice": if (input.invoice) { const label = `${input.invoice.invoicePrefix}${input.invoice.invoiceNumber}`; return `Added ${input.hours}h @ $${input.rate}/hr ($${amount.toFixed(2)}) to ${label}`; } return `Added ${input.hours}h to invoice`; case "saved_no_invoice": return `Saved ${input.hours}h — no open invoice for this client.`; case "saved_no_client": return `Saved ${input.hours}h — pick a client and invoice to bill.`; case "zero_hours": return "Timer stopped."; } }