diff --git a/frontend/src/components/crypto/CryptoTransactionModal.tsx b/frontend/src/components/crypto/CryptoTransactionModal.tsx index 74e7ef2..f09d228 100644 --- a/frontend/src/components/crypto/CryptoTransactionModal.tsx +++ b/frontend/src/components/crypto/CryptoTransactionModal.tsx @@ -4,6 +4,7 @@ import { Button } from "@/components/ui/Button"; import { Dialog } from "@/components/ui/Dialog"; import { Input, Label } from "@/components/ui/Input"; import { useAddCryptoTransaction, useUpdateCryptoTransaction } from "@/hooks/useCrypto"; +import { trimTrailingZeros } from "@/lib/format"; import { useTranslation } from "@/lib/i18n"; import { cn } from "@/lib/utils"; import type { CryptoHolding, CryptoTransaction, CryptoTransactionType } from "@/types"; @@ -39,8 +40,11 @@ export function CryptoTransactionModal({ open, onClose, holding, transaction = n if (!open) return; if (transaction) { setType(transaction.type); - setQuantity(transaction.quantity); - setPricePerUnit(transaction.price_per_unit); + // The API returns these at full Numeric(38,18) scale (e.g. + // "3.000000000000000000") — trimmed here so the field shows what was + // actually entered, not a wall of padding zeros. + setQuantity(trimTrailingZeros(transaction.quantity)); + setPricePerUnit(trimTrailingZeros(transaction.price_per_unit)); setDate(transaction.date); setNote(transaction.note ?? ""); } else { diff --git a/frontend/src/lib/format.ts b/frontend/src/lib/format.ts index 666e95b..5db7013 100644 --- a/frontend/src/lib/format.ts +++ b/frontend/src/lib/format.ts @@ -32,6 +32,20 @@ export function maskAmount(formatted: string, hidden: boolean): string { return hidden ? "••••" : formatted; } +/** Strips trailing zeros from a decimal string for pre-filling an editable + * number input — the backend stores crypto quantity/price as Numeric(38,18) + * and returns it at full scale (e.g. "3.000000000000000000"), which is + * correct for computation but not something anyone wants to see or edit + * around in a form field. Works on the string directly rather than via + * Number(), which would round a genuine 18-decimal-place amount (a + * wei-level token quantity) through floating point instead of just + * trimming the padding. */ +export function trimTrailingZeros(value: string): string { + if (!value.includes(".")) return value; + const trimmed = value.replace(/0+$/, "").replace(/\.$/, ""); + return trimmed === "" || trimmed === "-" ? "0" : trimmed; +} + const MONTH_LABELS_EN = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] as const; const MONTH_LABELS_RU = ["Янв", "Фев", "Мар", "Апр", "Май", "Июн", "Июл", "Авг", "Сен", "Окт", "Ноя", "Дек"] as const;