From df332802c0b604d68fbee4c53c7208d557d3a661 Mon Sep 17 00:00:00 2001 From: Bruno Bernardino Date: Mon, 3 Mar 2025 09:39:46 +0000 Subject: [PATCH] UX improvements for mobile expense input --- components/expenses/BudgetModal.tsx | 15 ++++++++++----- components/expenses/ExpenseModal.tsx | 9 +++++---- lib/utils/misc.ts | 4 ++++ lib/utils/misc_test.ts | 21 +++++++++++++++++++++ 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/components/expenses/BudgetModal.tsx b/components/expenses/BudgetModal.tsx index 8fc3e1f..632c378 100644 --- a/components/expenses/BudgetModal.tsx +++ b/components/expenses/BudgetModal.tsx @@ -2,6 +2,7 @@ import { useSignal } from '@preact/signals'; import { useEffect } from 'preact/hooks'; import { Budget } from '/lib/types.ts'; +import { formatInputToNumber } from '/lib/utils/misc.ts'; interface BudgetModalProps { isOpen: boolean; @@ -17,7 +18,7 @@ export default function BudgetModal( ) { const newBudgetName = useSignal(budget?.name ?? ''); const newBudgetMonth = useSignal(budget?.month ?? new Date().toISOString().substring(0, 10)); - const newBudgetValue = useSignal(budget?.value ?? 100); + const newBudgetValue = useSignal(budget?.value ?? 100); const resetForm = () => { newBudgetName.value = ''; @@ -85,14 +86,13 @@ export default function BudgetModal( { - newBudgetValue.value = Number(event.currentTarget.value); + newBudgetValue.value = event.currentTarget.value; }} - min='0' inputmode='decimal' placeholder='100' /> @@ -117,7 +117,12 @@ export default function BudgetModal( diff --git a/components/expenses/ExpenseModal.tsx b/components/expenses/ExpenseModal.tsx index 04c32a6..aa46445 100644 --- a/components/expenses/ExpenseModal.tsx +++ b/components/expenses/ExpenseModal.tsx @@ -2,6 +2,7 @@ import { useSignal } from '@preact/signals'; import { useEffect } from 'preact/hooks'; import { Budget, Expense } from '/lib/types.ts'; +import { formatInputToNumber } from '/lib/utils/misc.ts'; import { RequestBody as SuggestionsRequestBody, @@ -27,7 +28,7 @@ interface ExpenseModalProps { export default function ExpenseModal( { isOpen, expense, budgets, onClickSave, onClickDelete, onClose, shouldResetForm }: ExpenseModalProps, ) { - const newExpenseCost = useSignal(expense?.cost ?? ''); + const newExpenseCost = useSignal(expense?.cost ?? ''); const newExpenseDescription = useSignal(expense?.description ?? ''); const newExpenseBudget = useSignal(expense?.budget ?? 'Misc'); const newExpenseDate = useSignal(expense?.date ?? ''); @@ -112,12 +113,12 @@ export default function ExpenseModal( { - newExpenseCost.value = Number(event.currentTarget.value); + newExpenseCost.value = event.currentTarget.value; }} inputmode='decimal' placeholder='10.99' @@ -240,7 +241,7 @@ export default function ExpenseModal( class='px-5 py-2 bg-slate-700 hover:bg-slate-500 text-white cursor-pointer rounded-md ml-2' onClick={() => { onClickSave( - newExpenseCost.value as number, + formatInputToNumber(newExpenseCost.value), newExpenseDescription.value, newExpenseBudget.value, newExpenseDate.value, diff --git a/lib/utils/misc.ts b/lib/utils/misc.ts index 023cbff..d475baa 100644 --- a/lib/utils/misc.ts +++ b/lib/utils/misc.ts @@ -268,3 +268,7 @@ export function formatNumber(currency: SupportedCurrencySymbol, number: number) maximumFractionDigits: 2, }).format(Number.parseFloat(`${number}`.replace(',', '.'))); } + +export function formatInputToNumber(numberInput: number | string): number { + return Number.parseFloat(`${numberInput}`.replace(',', '.')); +} diff --git a/lib/utils/misc_test.ts b/lib/utils/misc_test.ts index 8874a9c..fcdd151 100644 --- a/lib/utils/misc_test.ts +++ b/lib/utils/misc_test.ts @@ -5,6 +5,7 @@ import { convertFormDataToObject, convertObjectToFormData, escapeHtml, + formatInputToNumber, formatNumber, generateHash, generateRandomCode, @@ -291,3 +292,23 @@ Deno.test('that formatNumber works', () => { assertEquals(result, test.expected); } }); + +Deno.test('that formatInputToNumber works', () => { + const tests: { input: number | string; expected: number }[] = [ + { input: 42, expected: 42 }, + { input: '42', expected: 42 }, + { input: 42.5, expected: 42.5 }, + { input: '42.5', expected: 42.5 }, + { input: '42,5', expected: 42.5 }, + { input: '1234,56', expected: 1234.56 }, + { input: 0, expected: 0 }, + { input: '0', expected: 0 }, + { input: '0,0', expected: 0 }, + { input: '0.0', expected: 0 }, + ]; + + for (const test of tests) { + const result = formatInputToNumber(test.input); + assertEquals(result, test.expected); + } +});