feat(03-01): enhance InlineEditCell with pencil icon hover affordance and save/error callbacks

- Add Pencil icon in display mode (opacity-0, group-hover:opacity-100)
- Add onSaveSuccess optional callback fired after successful save
- Add onSaveError optional callback fired + value reverted on save failure
- Wrap onSave in try/catch in handleBlur
This commit is contained in:
2026-03-11 22:31:20 +01:00
parent 58bb57b1cc
commit d9e60fa90c

View File

@@ -1,4 +1,5 @@
import { useState } from 'react'
import { Pencil } from 'lucide-react'
import { TableCell } from '@/components/ui/table'
import { Input } from '@/components/ui/input'
import { formatCurrency } from '@/lib/format'
@@ -8,10 +9,12 @@ interface InlineEditCellProps {
value: number
currency: string
onSave: (value: number) => Promise<void>
onSaveSuccess?: () => void
onSaveError?: () => void
className?: string
}
export function InlineEditCell({ value, currency, onSave, className }: InlineEditCellProps) {
export function InlineEditCell({ value, currency, onSave, onSaveSuccess, onSaveError, className }: InlineEditCellProps) {
const [editing, setEditing] = useState(false)
const [inputValue, setInputValue] = useState(String(value))
@@ -23,7 +26,13 @@ export function InlineEditCell({ value, currency, onSave, className }: InlineEdi
const handleBlur = async () => {
const num = parseFloat(inputValue)
if (!isNaN(num) && num !== value) {
try {
await onSave(num)
onSaveSuccess?.()
} catch {
setInputValue(String(value))
onSaveError?.()
}
}
setEditing(false)
}
@@ -49,10 +58,14 @@ export function InlineEditCell({ value, currency, onSave, className }: InlineEdi
/>
) : (
<span
className="cursor-pointer rounded px-2 py-1 hover:bg-muted"
className="group flex cursor-pointer items-center justify-end gap-1 rounded px-2 py-1 hover:bg-muted"
onClick={handleClick}
>
{formatCurrency(value, currency)}
<Pencil
data-testid="pencil-icon"
className="size-3 opacity-0 transition-opacity duration-150 group-hover:opacity-100 text-muted-foreground"
/>
</span>
)}
</TableCell>