mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 19:17:10 +00:00
* fix(runner): allow nil connection for web platform * fix(testrun): skip SDK handshake for web platform * feat(folio-web): add React/Vite web sample app * feat(folio-web): add sanderling spec * fix(chrome): use InsertText for multi-char text input * feat(hierarchy): add Screen field populated from sanderling-screen attr * fix(chrome): auto-detect viewport from CSS vars, fix InputText accumulation, expose route as screen * fix(runner): fall back to hierarchy root screen when snapshot screen is empty * fix(folio-web): broaden loggedIn extractor to all authenticated pages
68 lines
1.9 KiB
TypeScript
68 lines
1.9 KiB
TypeScript
import type { Transaction } from './db'
|
|
|
|
const CURRENCY = '$'
|
|
|
|
export function formatCents(
|
|
cents: number,
|
|
opts: { signed?: boolean } = {},
|
|
): string {
|
|
const abs = Math.abs(cents)
|
|
const dollars = Math.floor(abs / 100)
|
|
const rem = abs % 100
|
|
const dollarsStr = dollars.toLocaleString('en-US')
|
|
const formatted = `${CURRENCY}${dollarsStr}.${rem.toString().padStart(2, '0')}`
|
|
if (cents < 0) return `-${formatted}`
|
|
if (opts.signed && cents > 0) return `+${formatted}`
|
|
return formatted
|
|
}
|
|
|
|
export function parseCents(input: string): number | null {
|
|
const trimmed = input.trim().replace(/,/g, '')
|
|
if (trimmed === '') return null
|
|
if (!/^\d+(\.\d{1,2})?$/.test(trimmed)) return null
|
|
const [whole, frac = ''] = trimmed.split('.')
|
|
const fracPadded = (frac + '00').slice(0, 2)
|
|
const total = Number(whole) * 100 + Number(fracPadded)
|
|
if (!Number.isFinite(total) || !Number.isInteger(total)) return null
|
|
if (total > Number.MAX_SAFE_INTEGER) return null
|
|
return total
|
|
}
|
|
|
|
export function signedAmount(t: Transaction): number {
|
|
return t.type === 'credit' ? t.amount : -t.amount
|
|
}
|
|
|
|
export function balanceOf(txns: Transaction[]): number {
|
|
let total = 0
|
|
for (const t of txns) total += signedAmount(t)
|
|
return total
|
|
}
|
|
|
|
export function initialsOf(name: string): string {
|
|
const parts = name.trim().split(/\s+/).filter(Boolean)
|
|
if (parts.length === 0) return '?'
|
|
if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase()
|
|
return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase()
|
|
}
|
|
|
|
const DATE_FMT = new Intl.DateTimeFormat('en-US', {
|
|
month: 'short',
|
|
day: 'numeric',
|
|
hour: 'numeric',
|
|
minute: '2-digit',
|
|
})
|
|
|
|
export function formatDate(ts: number): string {
|
|
return DATE_FMT.format(new Date(ts))
|
|
}
|
|
|
|
const CLOCK_FMT = new Intl.DateTimeFormat('en-US', {
|
|
hour: 'numeric',
|
|
minute: '2-digit',
|
|
hour12: false,
|
|
})
|
|
|
|
export function formatClock(ts: number): string {
|
|
return CLOCK_FMT.format(new Date(ts))
|
|
}
|