69 lines
2.1 KiB
TypeScript
69 lines
2.1 KiB
TypeScript
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.";
|
|
}
|
|
}
|