mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-21 22:46:32 +00:00
72a82b3c44
214 items came out of writing the tests. Real defects are corrected, dead code is removed rather than covered, and the tests that pinned the old behaviour move with them. Also drops two agent coverage scratch files that slipped into the coverage PR and adds the ignore rule that keeps them out.
92 lines
3.9 KiB
TypeScript
92 lines
3.9 KiB
TypeScript
import { currencyDecimals } from '../../utils/formatters'
|
|
import { SYMBOLS, SPLIT_COLORS } from './BudgetPanel.constants'
|
|
|
|
export function widgetTheme(dark: boolean) {
|
|
if (dark) return {
|
|
bg: 'linear-gradient(180deg, #17171d 0%, #0d0d12 100%)',
|
|
border: 'rgba(255,255,255,0.07)',
|
|
text: '#ffffff',
|
|
sub: 'rgba(255,255,255,0.6)',
|
|
faint: 'rgba(255,255,255,0.4)',
|
|
track: 'rgba(255,255,255,0.04)',
|
|
divider: 'rgba(255,255,255,0.07)',
|
|
iconBg: 'rgba(255,255,255,0.08)',
|
|
iconBorder: 'rgba(255,255,255,0.12)',
|
|
iconColor: 'rgba(255,255,255,0.9)',
|
|
centerBg: '#17171d',
|
|
flowBg: 'rgba(255,255,255,0.05)',
|
|
flowBorder: 'rgba(255,255,255,0.07)',
|
|
flowHoverBg: 'rgba(255,255,255,0.08)',
|
|
flowHoverBorder: 'rgba(255,255,255,0.12)',
|
|
rowHover: 'rgba(255,255,255,0.03)',
|
|
shadow: '0 20px 50px rgba(0,0,0,0.35), inset 0 1px 0 rgba(255,255,255,0.04)',
|
|
donutShadow: 'drop-shadow(0 0 20px rgba(0,0,0,0.3))',
|
|
}
|
|
return {
|
|
bg: 'linear-gradient(180deg, #ffffff 0%, #f9fafb 100%)',
|
|
border: 'rgba(15,23,42,0.08)',
|
|
text: '#111827',
|
|
sub: 'rgba(17,24,39,0.6)',
|
|
faint: 'rgba(17,24,39,0.4)',
|
|
track: 'rgba(15,23,42,0.05)',
|
|
divider: 'rgba(15,23,42,0.08)',
|
|
iconBg: 'rgba(15,23,42,0.05)',
|
|
iconBorder: 'rgba(15,23,42,0.1)',
|
|
iconColor: 'rgba(17,24,39,0.75)',
|
|
centerBg: '#ffffff',
|
|
flowBg: 'rgba(15,23,42,0.03)',
|
|
flowBorder: 'rgba(15,23,42,0.08)',
|
|
flowHoverBg: 'rgba(15,23,42,0.06)',
|
|
flowHoverBorder: 'rgba(15,23,42,0.14)',
|
|
rowHover: 'rgba(15,23,42,0.04)',
|
|
shadow: '0 12px 32px rgba(15,23,42,0.08), 0 2px 6px rgba(0,0,0,0.04)',
|
|
donutShadow: 'drop-shadow(0 4px 18px rgba(15,23,42,0.12))',
|
|
}
|
|
}
|
|
|
|
export function hexLighten(hex: string, amount: number): string {
|
|
const m = hex.replace('#', '').match(/.{2}/g)
|
|
if (!m || m.length !== 3) return hex
|
|
const mix = (c: number) => Math.min(255, Math.round(c + (255 - c) * amount))
|
|
const [r, g, b] = m.map(x => parseInt(x, 16))
|
|
return `#${[mix(r), mix(g), mix(b)].map(v => v.toString(16).padStart(2, '0')).join('')}`
|
|
}
|
|
|
|
export const fmtNum = (v: number | null | undefined, locale: string, cur: string) => {
|
|
if (v == null || isNaN(v)) return '-'
|
|
const d = currencyDecimals(cur)
|
|
return Number(v).toLocaleString(locale, { minimumFractionDigits: d, maximumFractionDigits: d }) + ' ' + (SYMBOLS[cur] || cur)
|
|
}
|
|
|
|
type NumOrNull = number | null | undefined
|
|
|
|
export const calcPP = (p: NumOrNull, n: NumOrNull) => (n! > 0 ? (p as number) / (n as number) : null)
|
|
export const calcPD = (p: NumOrNull, d: NumOrNull) => (d! > 0 ? (p as number) / (d as number) : null)
|
|
export const calcPPD = (p: NumOrNull, n: NumOrNull, d: NumOrNull) => (n! > 0 && d! > 0 ? (p as number) / ((n as number) * (d as number)) : null)
|
|
|
|
// A custom (uneven) split has no single "per person" figure — one member's share
|
|
// differs from another's — so the averaged per-person columns are meaningless for it
|
|
// (the per-member amounts are shown via the member chips instead). #1458
|
|
export const hasCustomMemberSplit = (item: { members?: { amount?: number | null }[] }) =>
|
|
(item.members || []).some(m => m.amount != null)
|
|
|
|
export function splitColorFor(userId: number, order: number) {
|
|
return SPLIT_COLORS[order % SPLIT_COLORS.length]
|
|
}
|
|
|
|
export function colorForUserId(userId: number) {
|
|
return SPLIT_COLORS[((userId | 0) - 1 + SPLIT_COLORS.length * 1000) % SPLIT_COLORS.length]
|
|
}
|
|
|
|
/**
|
|
* Normalises a pasted amount to a plain `1234.56` string: drops currency
|
|
* symbols and spaces, treats the last comma/dot as the decimal separator and
|
|
* removes every thousand separator before it.
|
|
*/
|
|
export function normalizePastedAmount(raw: string): string {
|
|
const text = raw.trim().replace(/[^\d.,-]/g, '')
|
|
const decimalPos = Math.max(text.lastIndexOf(','), text.lastIndexOf('.'))
|
|
if (decimalPos === -1) return text.replace(/[.,]/g, '')
|
|
return text.substring(0, decimalPos).replace(/[.,]/g, '') + '.' + text.substring(decimalPos + 1)
|
|
}
|