feat(birikim): birikimler sayfası, varlık ayrıntısı ve alım/satım

- Portföy özeti, kâr/zarar, dağılım halka grafiği, kur uyarıları
- Varlık ayrıntısı: alım/satım formu (tutar önerisi), geçmiş, düzenle/sil
- Elle kur girme/kaldırma, döviz ekleme, pasife alma
- Miktar/kur alanı (6 ondalık, Türkçe biçim)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-13 01:14:50 +03:00
parent 3f60efdc33
commit 6b7b69c987
22 changed files with 1601 additions and 4 deletions
+43
View File
@@ -0,0 +1,43 @@
import type { ComponentProps } from 'react'
import { Input } from '@/components/ui/input'
import { formatE6, parseE6 } from '@/lib/decimal'
import { cn } from '@/lib/utils'
type Props = Omit<ComponentProps<typeof Input>, 'value' | 'onChange' | 'type'> & {
value: string
onChange: (value: string) => void
/** Sağda gösterilen birim: `gr`, `$`, `₺`… */
suffix: string
}
/**
* Miktar / kur alanı (6 ondalığa kadar). `MoneyInput` gibi: sayısal klavye, alandan çıkınca
* Türkçe biçim (`5.5` → `5,5`, `1000` → `1.000`). Değer metin; `parseE6` ile × 1e6'ya çevrilir.
*/
export function DecimalInput({ value, onChange, onBlur, suffix, className, ...props }: Props) {
return (
<div className="relative">
<Input
{...props}
type="text"
inputMode="decimal"
autoComplete="off"
value={value}
onChange={(e) => onChange(e.target.value)}
onBlur={(e) => {
const v = parseE6(value)
if (v !== null && value.trim() !== '') onChange(formatE6(v))
onBlur?.(e)
}}
className={cn('money text-right', className)}
style={{ paddingRight: `${Math.max(2.25, suffix.length * 0.6 + 1.25)}rem` }}
/>
<span
aria-hidden
className="pointer-events-none absolute inset-y-0 right-3 flex items-center text-muted-foreground"
>
{suffix}
</span>
</div>
)
}