Download apps/dashboard/src/utils/format.ts from AUXteam/Midday: direct link, hf CLI and curl.
- Browser
- Download file 5.45 kB
-
https://huggingface.co/spaces/AUXteam/Midday/resolve/main/apps/dashboard/src/utils/format.ts
- Command line
-
hf download hf://spaces/AUXteam/Midday/apps/dashboard/src/utils/format.ts
-
curl -L -o format.ts https://huggingface.co/spaces/AUXteam/Midday/resolve/main/apps/dashboard/src/utils/format.ts
5.45 kB
| import { TZDate } from "@date-fns/tz"; | |
| import { | |
| differenceInDays, | |
| differenceInMonths, | |
| format, | |
| startOfDay, | |
| } from "date-fns"; | |
| import { normalizeCurrencyCode } from "./currency"; | |
| export function formatSize(bytes: number): string { | |
| const units = ["byte", "kilobyte", "megabyte", "gigabyte", "terabyte"]; | |
| const unitIndex = Math.max( | |
| 0, | |
| Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1), | |
| ); | |
| return Intl.NumberFormat("en-US", { | |
| style: "unit", | |
| unit: units[unitIndex], | |
| }).format(+Math.round(bytes / 1024 ** unitIndex)); | |
| } | |
| type FormatAmountParams = { | |
| currency: string; | |
| amount: number; | |
| locale?: string | null; | |
| maximumFractionDigits?: number; | |
| minimumFractionDigits?: number; | |
| }; | |
| export function formatAmount({ | |
| currency, | |
| amount, | |
| locale = "en-US", | |
| minimumFractionDigits, | |
| maximumFractionDigits, | |
| }: FormatAmountParams) { | |
| if (!currency) { | |
| return; | |
| } | |
| // Normalize currency code to ISO 4217 format | |
| const normalizedCurrency = normalizeCurrencyCode(currency); | |
| // Fix: locale can be null, but Intl.NumberFormat expects string | string[] | undefined | |
| // So, if locale is null, pass undefined instead | |
| const safeLocale = locale ?? undefined; | |
| try { | |
| return Intl.NumberFormat(safeLocale, { | |
| style: "currency", | |
| currency: normalizedCurrency, | |
| minimumFractionDigits, | |
| maximumFractionDigits, | |
| }).format(amount); | |
| } catch (error) { | |
| // Fallback to USD if currency is invalid | |
| console.warn( | |
| `Invalid currency code: ${currency} (normalized to ${normalizedCurrency}), falling back to USD`, | |
| error, | |
| ); | |
| return Intl.NumberFormat(safeLocale, { | |
| style: "currency", | |
| currency: "USD", | |
| minimumFractionDigits, | |
| maximumFractionDigits, | |
| }).format(amount); | |
| } | |
| } | |
| export function secondsToHoursAndMinutes(seconds: number) { | |
| const hours = Math.floor(seconds / 3600); | |
| const minutes = Math.floor((seconds % 3600) / 60); | |
| if (hours && minutes) { | |
| return `${hours}h ${minutes}m`; | |
| } | |
| if (hours) { | |
| return `${hours}h`; | |
| } | |
| if (minutes) { | |
| return `${minutes}m`; | |
| } | |
| return "0m"; | |
| } | |
| type BurnRateData = { | |
| value: number; | |
| date: string; | |
| }; | |
| export function calculateAvgBurnRate(data: BurnRateData[] | null) { | |
| if (!data) { | |
| return 0; | |
| } | |
| return data?.reduce((acc, curr) => acc + curr.value, 0) / data?.length; | |
| } | |
| export function formatAccountName({ | |
| name = "", | |
| currency, | |
| }: { | |
| name?: string; | |
| currency?: string | null; | |
| }) { | |
| if (currency) { | |
| return `${name} (${currency})`; | |
| } | |
| return name; | |
| } | |
| export function formatDateRange(dates: TZDate[]): string { | |
| if (!dates.length) return ""; | |
| const formatFullDate = (date: TZDate) => format(date, "MMM d"); | |
| const formatDay = (date: TZDate) => format(date, "d"); | |
| const startDate = dates[0]; | |
| const endDate = dates[1]; | |
| if (!startDate) return ""; | |
| if ( | |
| dates.length === 1 || | |
| !endDate || | |
| startDate.getTime() === endDate.getTime() | |
| ) { | |
| return formatFullDate(startDate); | |
| } | |
| if (startDate.getMonth() === endDate.getMonth()) { | |
| // Same month | |
| return `${format(startDate, "MMM")} ${formatDay(startDate)} - ${formatDay(endDate)}`; | |
| } | |
| // Different months | |
| return `${formatFullDate(startDate)} - ${formatFullDate(endDate)}`; | |
| } | |
| export function getDueDateStatus(dueDate: string): string { | |
| // Parse due date as UTC (it's stored as UTC midnight) | |
| const due = new TZDate(dueDate, "UTC"); | |
| // Get current date in UTC for consistent comparison | |
| const now = new Date(); | |
| const nowUTC = new TZDate(now.toISOString(), "UTC"); | |
| // Compare at the day level in UTC | |
| const nowDay = startOfDay(nowUTC); | |
| const dueDay = startOfDay(due); | |
| const diffDays = differenceInDays(dueDay, nowDay); | |
| const diffMonths = differenceInMonths(dueDay, nowDay); | |
| if (diffDays === 0) return "Today"; | |
| if (diffDays === 1) return "Tomorrow"; | |
| if (diffDays === -1) return "Yesterday"; | |
| if (diffDays > 0) { | |
| if (diffMonths < 1) return `in ${diffDays} days`; | |
| return `in ${diffMonths} month${diffMonths === 1 ? "" : "s"}`; | |
| } | |
| if (diffMonths < 1) | |
| return `${Math.abs(diffDays)} day${Math.abs(diffDays) === 1 ? "" : "s"} ago`; | |
| return `${diffMonths} month${diffMonths === 1 ? "" : "s"} ago`; | |
| } | |
| export function formatRelativeTime(date: Date): string { | |
| const now = new Date(); | |
| const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000); | |
| if (diffInSeconds < 60) { | |
| return "just now"; | |
| } | |
| const intervals = [ | |
| { label: "y", seconds: 31536000 }, | |
| { label: "mo", seconds: 2592000 }, | |
| { label: "d", seconds: 86400 }, | |
| { label: "h", seconds: 3600 }, | |
| { label: "m", seconds: 60 }, | |
| ] as const; | |
| for (const interval of intervals) { | |
| const count = Math.floor(diffInSeconds / interval.seconds); | |
| if (count > 0) { | |
| return `${count}${interval.label} ago`; | |
| } | |
| } | |
| return "just now"; | |
| } | |
| export function formatCompactAmount( | |
| amount: number, | |
| locale?: string | null, | |
| ): string { | |
| const absAmount = Math.abs(amount); | |
| const safeLocale = locale ?? "en-US"; | |
| if (absAmount >= 1000000) { | |
| const formatted = (absAmount / 1000000).toLocaleString(safeLocale, { | |
| minimumFractionDigits: 1, | |
| maximumFractionDigits: 1, | |
| }); | |
| return `${formatted}m`; | |
| } | |
| // Always show in thousands notation | |
| const formatted = (absAmount / 1000).toLocaleString(safeLocale, { | |
| minimumFractionDigits: 1, | |
| maximumFractionDigits: 1, | |
| }); | |
| return `${formatted}k`; | |
| } | |