chore: align utxo status tooltip

This commit is contained in:
theborakompanioni 2026-02-18 19:06:59 +01:00
parent 051ff0fc8c
commit 6054c2e989
No known key found for this signature in database
GPG key ID: E8070AF0053AAC0D
6 changed files with 70 additions and 60 deletions

View file

@ -370,7 +370,9 @@ export const OrderbookContent = ({ enabled, className }: OrderbookContentProps)
</CardHeader>
<CardContent className="text-muted-foreground grid gap-1 text-sm sm:grid-cols-2">
{marketSummary.medianAbsFee !== null && (
<p>{t('orderbook.market_summary_median_abs_fee', { value: marketSummary.medianAbsFee.toLocaleString() })}</p>
<p>
{t('orderbook.market_summary_median_abs_fee', { value: marketSummary.medianAbsFee.toLocaleString() })}
</p>
)}
{marketSummary.medianRelFee !== null && (
<p>

View file

@ -2,17 +2,32 @@ import * as React from 'react'
import { Slot } from '@radix-ui/react-slot'
import type { VariantProps } from 'class-variance-authority'
import { cn } from '@/lib/utils'
import { Tooltip, TooltipContent, TooltipTrigger } from '../tooltip'
import { statusBadgeVariants } from './StatusBadge-variants'
function StatusBadge({
className,
variant,
asChild = false,
tooltip,
...props
}: React.ComponentProps<'span'> & VariantProps<typeof statusBadgeVariants> & { asChild?: boolean }) {
}: React.ComponentProps<'span'> &
VariantProps<typeof statusBadgeVariants> & {
asChild?: boolean
tooltip?: React.ReactNode | string
}) {
const Comp = asChild ? Slot : 'span'
return <Comp data-slot="badge" className={cn(statusBadgeVariants({ variant }), className)} {...props} />
const element = <Comp data-slot="badge" className={cn(statusBadgeVariants({ variant }), className)} {...props} />
return !tooltip ? (
element
) : (
<Tooltip key={props.key}>
<TooltipTrigger asChild>{element}</TooltipTrigger>
<TooltipContent>{tooltip}</TooltipContent>
</Tooltip>
)
}
export { StatusBadge }

View file

@ -154,11 +154,16 @@ export const BranchEntryTable = ({
header: () => t(/* TODO: i18n keys */ 'jar_details.utxo_list.column_title_label_and_status'),
cell: (info) => (
<div className="flex items-center gap-2">
{info.row.original.tags.map((it, index) => (
<StatusBadge key={index} variant={it.variant}>
{it.displayValue}
</StatusBadge>
))}
{info.row.original.tags.map((it, index) => {
const tooltipKey = `jar_details.utxo_list.utxo_tag_tooltip_${it.value}`
const tooltip = t(tooltipKey)
const hasTooltip = tooltip !== tooltipKey
return (
<StatusBadge key={index} variant={it.variant} tooltip={hasTooltip ? tooltip : undefined}>
{it.displayValue}
</StatusBadge>
)
})}
</div>
),
enableSorting: false,

View file

@ -39,7 +39,6 @@ import { Balance } from '../ui/jam/Balance'
import { CopyButton } from '../ui/jam/CopyButton'
import { SortIcon } from '../ui/jam/SortIcon'
import { StatusBadge } from '../ui/jam/StatusBadge'
import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip'
const ITEMS_PER_PAGE = 25
@ -241,15 +240,8 @@ const utxoTableColumns = (t: TFunction): ColumnDef<UtxoTableEntry, any>[] => {
const tooltipKey = `jar_details.utxo_list.utxo_tag_tooltip_${it.value}`
const tooltip = t(tooltipKey)
const hasTooltip = tooltip !== tooltipKey
return hasTooltip ? (
<Tooltip key={index}>
<TooltipTrigger asChild>
<StatusBadge variant={it.variant}>{it.displayValue}</StatusBadge>
</TooltipTrigger>
<TooltipContent>{tooltip}</TooltipContent>
</Tooltip>
) : (
<StatusBadge key={index} variant={it.variant}>
return (
<StatusBadge key={index} variant={it.variant} tooltip={hasTooltip ? tooltip : undefined}>
{it.displayValue}
</StatusBadge>
)

View file

@ -187,6 +187,12 @@ export const UtxosContent = ({ enabled, walletFileName, addressSummary, jar }: U
return (
<>
{reusedCount > 0 && (
<Alert variant="destructive">
<AlertTriangleIcon />
<AlertDescription>{t('jar_details.utxo_list.alert_reused_address', { count: reusedCount })}</AlertDescription>
</Alert>
)}
<div className="flex flex-col text-sm sm:flex-row sm:gap-2">
<div className="flex flex-1 gap-2">
{t('jar_details.utxo_list.title', { count: jar.utxos.length, jar: jar.name })}
@ -197,37 +203,37 @@ export const UtxosContent = ({ enabled, walletFileName, addressSummary, jar }: U
</Trans>
</div>
</div>
<div className={cn('flex items-center gap-2', {})}>
<Button
size="sm"
disabled={!operationsEnabled || walletInfo.isFetching}
onClick={() => void walletInfo.refetch()}
>
<RefreshCwIcon className={cn({ 'motion-safe:animate-spin': walletInfo.isFetching })} />
{t('global.refresh')}
</Button>
<ButtonGroup>
<div className="flex flex-col justify-between gap-2 sm:flex-row sm:items-center">
<div className="flex items-center gap-2">
<Button
size="sm"
variant={selectedUtxos.length === 0 ? 'outline' : undefined}
disabled={!operationsEnabled || selectedUtxos.length === 0 || allSelectedUtxosFrozen}
onClick={() => void onFreezeClick()}
disabled={!operationsEnabled || walletInfo.isFetching}
onClick={() => void walletInfo.refetch()}
>
{freezeUtxos.isPending ? <Spinner /> : <ThermometerSnowflakeIcon />}
{t('jar_details.utxo_list.button_freeze')}
<RefreshCwIcon className={cn({ 'motion-safe:animate-spin': walletInfo.isFetching })} />
{t('global.refresh')}
</Button>
<Button
size="sm"
variant={selectedUtxos.length === 0 ? 'outline' : undefined}
disabled={!operationsEnabled || selectedUtxos.length === 0 || allSelectedUtxosUnfrozen}
onClick={() => void onUnfreezeClick()}
>
{unfreezeUtxos.isPending ? <Spinner /> : <ThermometerSunIcon />}
{t('jar_details.utxo_list.button_unfreeze')}
</Button>
</ButtonGroup>
</div>
<div className="flex items-center gap-2">
<ButtonGroup>
<Button
size="sm"
variant={selectedUtxos.length === 0 ? 'outline' : undefined}
disabled={!operationsEnabled || selectedUtxos.length === 0 || allSelectedUtxosFrozen}
onClick={() => void onFreezeClick()}
>
{freezeUtxos.isPending ? <Spinner /> : <ThermometerSnowflakeIcon />}
{t('jar_details.utxo_list.button_freeze')}
</Button>
<Button
size="sm"
variant={selectedUtxos.length === 0 ? 'outline' : undefined}
disabled={!operationsEnabled || selectedUtxos.length === 0 || allSelectedUtxosUnfrozen}
onClick={() => void onUnfreezeClick()}
>
{unfreezeUtxos.isPending ? <Spinner /> : <ThermometerSunIcon />}
{t('jar_details.utxo_list.button_unfreeze')}
</Button>
</ButtonGroup>
</div>
<Input
placeholder={t('jar_details.utxo_list.placeholder_search')}
value={searchFilter}
@ -241,14 +247,6 @@ export const UtxosContent = ({ enabled, walletFileName, addressSummary, jar }: U
globalFilter={searchFilter}
onRowSelectionChange={setRowSelection}
/>
{reusedCount > 0 && (
<Alert variant="destructive">
<AlertTriangleIcon />
<AlertDescription>
{t('jar_details.utxo_list.alert_reused_address', { count: reusedCount })}
</AlertDescription>
</Alert>
)}
</>
)
}

View file

@ -808,17 +808,15 @@
"utxo_tooltip_locktime": "locked until {{ locktime }}",
"utxo_tag_frozen": "frozen",
"utxo_tag_fb": "bond",
"utxo_tag_tooltip_cj-out": "CoinJoin output — good privacy",
"utxo_tag_tooltip_non-cj-change": "Change from a non-CoinJoin tx — may be linkable to inputs",
"utxo_tag_tooltip_cj-out": "CoinJoin output",
"utxo_tag_tooltip_non-cj-change": "Change from a non-CoinJoin tx",
"utxo_tag_tooltip_change-out": "Change from a CoinJoin tx",
"utxo_tag_tooltip_reused": "Address used more than once — bad for privacy",
"utxo_tag_tooltip_reused": "Address used more than once",
"utxo_tag_tooltip_deposit": "Received from an external source",
"utxo_tag_tooltip_frozen": "Excluded from spending",
"utxo_tag_tooltip_locked": "Excluded from spending — may be reserved for a CoinJoin",
"utxo_tag_tooltip_locked": "Time-locked by the Bitcoin protocol",
"utxo_tag_tooltip_pending": "Part of a pending transaction",
"utxo_tag_tooltip_bond": "Locked as a fidelity bond",
"utxo_tag_tooltip_new": "Fresh output that has not yet been used in a transaction",
"utxo_tag_tooltip_used": "Output associated with prior usage or address reuse",
"utxo_tag_tooltip_bond": "Fidelity bond",
"column_title_balance": "Value",
"column_title_address": "Address",
"column_title_confirmations": "Confs.",